用于从类中生成新对象的惯用Python

TR.*_*TR. 14 python

我在我的一个对象上有一个方法,它返回同一个类的新实例.我试图找出最惯用的方法来编写这种方法,以便它生成一个相同类型的新对象,而无需复制代码.

由于此方法使用来自实例的数据,因此我的第一个传递是:

class Foo(object):
    def get_new(self):
        data = # Do interesting things
        return Foo(data)
Run Code Online (Sandbox Code Playgroud)

但是,如果我将Foo子类化并且不覆盖get_new,则在SubFoo上调用get_new将返回Foo!所以,我可以写一个classmethod:

class Foo(object):

    @classmethod
    def get_new(cls, obj):
        data = # Munge about in objects internals
        return cls(data)
Run Code Online (Sandbox Code Playgroud)

但是,我正在访问的数据是特定于对象的,所以它似乎打破了封装,因为它不是一个"正常"(未修饰)的方法.此外,你必须称之为SubFoo.get_new(sub_foo_inst),这似乎是多余的.我希望对象能够"知道"返回哪种类型 - 与自身类型相同!

我想也可以在类中添加一个工厂方法,并在任何地方覆盖返回类型,而不重复逻辑,但这似乎在子类上做了很多工作.

所以,我的问题是,编写一个方法的最佳方法是什么,它提供了类的类型的灵活性,而无需在整个地方注释类型?

got*_*nes 19

如果要使子类化更灵活,可以使用self.__class__特殊属性:

class Foo(object):
    def __init__(self, data):
        self.data = data

    def get_new(self):
        data = # Do interesting things
        return self.__class__(data)
Run Code Online (Sandbox Code Playgroud)

请注意,使用该@classmethod方法将阻止您访问任何一个实例中的数据,将其作为#Do interesting things依赖于实例中存储的数据的实例中的可行解决方案.

对于Python 2,我不建议使用type(self),因为这会为经典类返回一个不合适的值(即那些没有从基类中继承的类object):

>>> class Foo:
...     pass
... 
>>> f = Foo()
>>> type(f)
<type 'instance'>
>>> f.__class__    # Note that the __class__ attribute still works
<class '__main__.Foo'>
Run Code Online (Sandbox Code Playgroud)

对于Python 3来说,这并不是一个问题,因为所有的类都来源于object,但是,我认为它self.__class__被认为是更多的Pythonic成语.

  • 正如 möter 所建议的那样,使用 `type(self)` 基本上可以做同样的事情,但恕我直言稍微干净一点。但也许这只是我对带有四个下划线的任何东西的厌恶。 (3认同)
  • @Thomas:我实际上不建议使用`type()`,我已经为我的答案添加了解释原因. (3认同)