2

请原谅 Python 新手在风格上的无能。

我有一个类,它采用一个参数来建立初始数据。初始数据有两种进入方式:字符串列表,或带有字符串键和整数值的字典。

现在我只实现了一个版本的构造函数,一个以字典为参数的版本,以 {} 作为默认值。列表参数init作为方法实现,即

myClass = MyClass()
myClass.initList(listVar)

我当然可以忍受这个,但这肯定不是完美的。所以,我决定在这里寻求一些 Python 的智慧:应该如何实现这样的多态构造函数?如果这是字典,我是否应该尝试读取 initData.keys() 以嗅探失败?或者也许嗅探参数类型以实现糟糕的多态性,而设计上不欢迎它被认为是非pythonic?

4

4 回答 4

3

在理想的世界中,您会编写一个构造函数,它可以采用alistdict不知道区别(即duck 类型)。当然,这不太现实,因为它们是完全不同的鸭子。

可以理解的是,您对检查实际实例类型的想法也有一点心痛,因为它与鸭子类型的想法背道而驰。但是,在 python 2.6abc中引入了一个有趣的模块,它允许定义“抽象基类”。要被视为抽象基类的实例,实际上不必从它继承,而只需实现其所有抽象方法。

collections模块包括一些在这里感兴趣的抽象基类,即collections.Sequencecollections.Mapping。因此,您可以编写__init__如下函数:

def __init__(self, somedata):
    if isinstance(somedata, collections.Sequence):
        # somedata is a list or some other Sequence
    elif isinstance(somedata, collections.Mapping):
        # somedata is a dict or some other Mapping

http://docs.python.org/2/library/collections.html#collections-abstract-base-classes包含每个 ABC 提供哪些方法的细节。如果您坚持这些,那么您的代码现在可以接受任何适合这些抽象基类之一的对象。而且,就内置dictlist类型而言,您可以看到:

>>> isinstance([], collections.Sequence)
True
>>> isinstance([], collections.Mapping)
False
>>> isinstance({}, collections.Sequence)
False
>>> isinstance({}, collections.Mapping)
True

而且,几乎是偶然的,你也让它起作用tuple了。您可能并不关心它是否真的是 a list,只是您可以从中读取元素。但是,如果您检查过,isinstance(somedata, list)您将排除tuple. 这就是使用 ABC 给你带来的东西。

于 2013-03-15T15:40:14.737 回答
2

正如@Jan-PhilipGehrcke 指出的那样,pythonic很难量化。对我来说这意味着:

  • 易于阅读
  • 易于维护
  • 简单胜于复杂胜于复杂
  • 等等,等等(查看Zen of Python完整列表,您可以通过import this在解释器中输入来获得)

因此,最 Pythonic 的解决方案取决于您必须为每个受支持的初始化程序做什么,以及您拥有多少个初始化程序。我会说,如果你只有少数几个,并且每一个都可以通过几行代码来处理,那么使用isinstanceand __init__

class MyClass(object):

    def __init__(self, initializer):
        """
        initialize internal data structures with 'initializer'
        """
        if isinstance(initializer, dict):
            for k, v in itit_dict.items():
                # do something with k & v
                setattr(self, k, v)
        elif isinstance(initializer, (list, tuple)):
            for item in initializer:
                setattr(self, item, None)

另一方面,如果您有许多可能的初始化程序,或者其中任何一个需要大量代码来处理,那么您将希望classmethod为每种可能的初始化类型都有一个构造函数,最常见的用法是__init__

class MyClass(object):

    def __init__(self, init_dict={}):
        """
        initialize internal data structures with 'init_dict'
        """
        for k, v in itit_dict.items():
            # do something with k & v
            setattr(self, k, v)

    @classmethod
    def from_sequence(cls, init_list):
        """
        initialize internal  data structures with 'init_list'
        """
        result = cls()
        for item in init_list:
            setattr(result, item, None)
        return result

这使每个可能的构造函数都简单、干净且易于理解。

附带说明:使用可变对象作为默认值(就像我在上面__init__所做的那样)需要小心;原因是默认值只评估一次,然后无论结果如何,都将用于每次后续调用。只有当您在函数中修改该对象时,这才是一个问题,因为这些修改将在每个后续调用中看到——除非您想要创建一个可能不是您正在寻找的行为的缓存。这对我的示例来说不是问题,因为我没有修改init_dict,只是迭代它(如果调用者没有替换它,因为它是空的,这是一个无操作)。

于 2013-03-15T14:33:19.393 回答
2

Python中没有函数重载if isinstance(...)在您的方法中使用__init__()将非常易于阅读和理解:

class Foo(object):
    def __init__(self, arg):
        if isinstance(arg, dict):
            ...
        elif isinstance(arg, list):
            ...
        else:
            raise Exception("big bang")
于 2013-03-15T14:02:43.867 回答
-1

您可以使用*args**kwargs执行此操作。但是如果你想知道你应该使用什么类型的参数,type()或者isinstance()

于 2013-03-15T14:04:27.677 回答