当前位置:  开发笔记 > 编程语言 > 正文

什么是在Python中拥有多个构造函数的干净,pythonic方式?

如何解决《什么是在Python中拥有多个构造函数的干净,pythonic方式?》经验,为你挑选了8个好方法。

我无法找到明确的答案.AFAIK,你不能__init__在Python类中拥有多个函数.那么我该如何解决这个问题呢?

假设我有一个Cheese使用该number_of_holes属性调用的类.我怎样才能有两种创建奶酪对象的方法......

    一个像这样的洞: parmesan = Cheese(num_holes = 15)

    并且不带参数并且只是随机化number_of_holes属性:gouda = Cheese()

我只想到一种方法来做到这一点,但这似乎有点笨重:

class Cheese():
    def __init__(self, num_holes = 0):
        if (num_holes == 0):
            # randomize number_of_holes
        else:
            number_of_holes = num_holes

你说什么?还有另外一种方法吗?



1> vartec..:

实际上None对于"神奇"的价值要好得多:

class Cheese():
    def __init__(self, num_holes = None):
        if num_holes is None:
            ...

现在,如果您想完全自由地添加更多参数:

class Cheese():
    def __init__(self, *args, **kwargs):
        #args -- tuple of anonymous arguments
        #kwargs -- dictionary of named arguments
        self.num_holes = kwargs.get('num_holes',random_holes())

为了更好地解释*args**kwargs(你可以实际更改这些名称)的概念:

def f(*args, **kwargs):
   print 'args: ', args, ' kwargs: ', kwargs

>>> f('a')
args:  ('a',)  kwargs:  {}
>>> f(ar='a')
args:  ()  kwargs:  {'ar': 'a'}
>>> f(1,2,param=3)
args:  (1, 2)  kwargs:  {'param': 3}

http://docs.python.org/reference/expressions.html#calls


对于那些感兴趣的人,`kwargs`代表*关键字参数*(一旦你知道它就显得逻辑).:)
有些时候,`* args`和`** kwargs`可能会过大。在大多数构造函数中,您想知道您的参数是什么。

2> Ber..:

num_holes=None如果你想要的话,使用默认是很好的__init__.

如果您需要多个独立的"构造函数",则可以将它们作为类方法提供.这些通常称为工厂方法.在这种情况下,你可以有默认num_holesBE 0.

class Cheese(object):
    def __init__(self, num_holes=0):
        "defaults to a solid cheese"
        self.number_of_holes = num_holes

    @classmethod
    def random(cls):
        return cls(randint(0, 100))

    @classmethod
    def slightly_holey(cls):
        return cls(randint(0, 33))

    @classmethod
    def very_holey(cls):
        return cls(randint(66, 100))

现在创建这样的对象:

gouda = Cheese()
emmentaler = Cheese.random()
leerdammer = Cheese.slightly_holey()


我认为这也更清洁.这是一个更清晰的解释的链接,恕我直言 - 来自comp.lang.python帖子,Alex Martelli的"Re:Multiple constructors".http://coding.derkeiler.com/Archive/Python/comp.lang.python/2005-02/1294.html
@rmbianchi:接受的答案可能更符合其他语言,但它也不那么pythonic:`@ classmethod`s是实现多个contstructors的pythonic方式.
@Bepetersn有一些实例方法(普通方法),它们的实例对象被称为`self`.然后是类方法(使用`@ classmethod`),它们将类对象引用为`cls`.最后有一些静态方法(用`@ staticmethod`声明),它们都没有这些引用.静态方法就像模块级别的函数一样,除了它们存在于类的名称空间中.

3> Yes - that J..:

如果你想使用可选参数,所有这些答案都非常好,但另一种Pythonic可能是使用classmethod来生成工厂式伪构造函数:

def __init__(self, num_holes):

  # do stuff with the number

@classmethod
def fromRandom(cls):

  return cls( # some-random-number )



4> Ferdinand Be..:

为什么你认为你的解决方案"笨重"?就个人而言,我个人更喜欢一个默认值超过多个重载构造函数的构造函数(Python不支持方法重载):

def __init__(self, num_holes=None):
    if num_holes is None:
        # Construct a gouda
    else:
        # custom cheese
    # common initialization

对于具有许多不同构造函数的非常复杂的情况,使用不同的工厂函数可能更简洁:

@classmethod
def create_gouda(cls):
    c = Cheese()
    # ...
    return c

@classmethod
def create_cheddar(cls):
    # ...

在您的奶酪示例中,您可能想要使用奶酪的Gouda子类...


工厂函数使用_cls_:使用_cls_而不是_Cheese_.如果没有,使用类方法而不是静态方法有什么意义?

5> Brad C..:

这些是您实施的好主意,但如果您要向用户展示奶酪制作界面.他们不关心奶酪有多少孔或者是什么内部制作奶酪.你的代码的用户只想要"gouda"或"parmesean"吗?

那么为什么不这样做:

# cheese_user.py
from cheeses import make_gouda, make_parmesean

gouda = make_gouda()
paremesean = make_parmesean()

然后你可以使用上面的任何方法来实际实现这些功能:

# cheeses.py
class Cheese(object):
    def __init__(self, *args, **kwargs):
        #args -- tuple of anonymous arguments
        #kwargs -- dictionary of named arguments
        self.num_holes = kwargs.get('num_holes',random_holes())

def make_gouda():
    return Cheese()

def make_paremesean():
    return Cheese(num_holes=15)

这是一个很好的封装技术,我认为它更像Pythonic.对我来说,这种做事方式更适合鸭子打字.你只是要求一个gouda对象而你并不关心它是什么类.


`make_gouda,make_parmesan`应该是`class Cheese`的classmethods

6> Andrzej Pron..:

人们肯定更喜欢已发布的解决方案,但由于还没有人提到这个解决方案,我认为值得一提的是完整性.

@classmethod可以修改该方法以提供不调用默认构造函数(__init__)的替代构造函数.相反,使用创建实例__new__.

如果无法根据构造函数参数的类型选择初始化类型,并且构造函数不共享代码,则可以使用此方法.

例:

class MyClass(set):

    def __init__(self, filename):
        self._value = load_from_file(filename)

    @classmethod
    def from_somewhere(cls, somename):
        obj = cls.__new__(cls)  # Does not call __init__
        obj._value = load_from_somewhere(somename)
        return obj



7> mluebke..:

最好的答案就是上面关于默认参数的答案,但我写这篇文章很有意思,它确实符合"多个构造函数"的要求.使用风险由您自己承担.

方法怎么样?

"典型的实现通过调用超类的创建类的新实例使用超级(currentclass,CLS)()方法.(CLS [,...])用适当的参数,然后修改之前根据需要新创建的实例归还它."

因此,您可以通过附加适当的构造函数方法让方法修改类定义.

class Cheese(object):
    def __new__(cls, *args, **kwargs):

        obj = super(Cheese, cls).__new__(cls)
        num_holes = kwargs.get('num_holes', random_holes())

        if num_holes == 0:
            cls.__init__ = cls.foomethod
        else:
            cls.__init__ = cls.barmethod

        return obj

    def foomethod(self, *args, **kwargs):
        print "foomethod called as __init__ for Cheese"

    def barmethod(self, *args, **kwargs):
        print "barmethod called as __init__ for Cheese"

if __name__ == "__main__":
    parm = Cheese(num_holes=5)


这种代码让我做了关于使用动态语言进行噩梦的代码 - 并不是说​​它存在任何本质上的错误,只是它违反了我对类的一些关键假设.
@ Reti43说两个线程试图同时创建奶酪,一个用'Cheese(0)`和一个用`Cheese(1)`.线程1可能会运行`cls .__ init__ = cls.foomethod`,但是线程2可能会在线程1进一步运行之前运行`cls .__ init__ = cls.barmethod`.然后两个线程最终都会调用`barmethod`,这不是你想要的.

8> Devin Jeanpi..:

num_holes=None而是默认使用.然后检查是否num_holes is None,如果是,随机化.无论如何,这就是我通常看到的.

更完全不同的构造方法可能需要一种返回实例的类方法cls.

推荐阅读
跟我搞对象吧
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有