在类内生成新对象的Python惯用方式

17

我有一个对象上的方法,返回该类别的新实例。 我正在尝试找出编写此方法最惯用的方法,使其生成相同类型的新对象而不重复代码。

由于此方法使用实例的数据,因此我的第一次尝试是:

class Foo(object):
    def get_new(self):
        data = # Do interesting things
        return Foo(data)

然而,如果我继承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)
然而,我正在访问的数据是针对对象特定的,因此如果这不是一个“普通”的(未装饰的)方法,似乎会破坏封装性。此外,您还必须像这样调用它:SubFoo.get_new(sub_foo_inst),这似乎是多余的。我希望对象只需“知道”返回哪种类型——与自身相同的类型!
我想,还可以在类中添加一个工厂方法,并在各个地方覆盖返回类型,而不需要重复逻辑,但这似乎会给子类带来很多工作。
所以,我的问题是,如何编写一种方法,使其在不必在所有地方注释类型的情况下,能够灵活地处理类的类型?

classmethods在Python中并不是很惯用;工厂函数可以只是普通函数,它们存在于与类相同的模块中。 - Adam Vandenberg
1
@Adam:类方法有它们的用处(例如当您正在使用类变量并且不想破坏继承时)。静态方法要糟糕得多。 - user395760
2个回答

24

如果你想让它更具子类的灵活性,你可以简单地使用 self.__class__ 这个特殊属性:

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

    def get_new(self):
        data = # Do interesting things
        return self.__class__(data)
注意使用@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'>

对于Python 3来说,这不是什么大问题,因为所有类都派生自 object,但我认为self.__class__被认为是更加Pythonic的习惯用法。


太好了!那正是我在寻找的。这只是我认真使用Python的第二周左右,所以我还不完全了解所有的组件及其用法。 - TR.
3
像möter建议的那样使用type(self),实际上做的是同样的事情,但在我看来更加简洁。但也许这只是我对带有四个下划线的任何东西的厌恶。 - Thomas
3
@Thomas:实际上我不建议使用type(),我已经在我的回答中添加了原因解释。 - gotgenes
1
我同意@Thomas的观点。在Python 2已经死亡的时代,type(...)似乎更加清晰明了。不应该直接访问Dunder方法(否则通常不被认为是Pythonic),难道我错了吗? - Mayou36

3
你可以使用内置的'type'。
type(instance)

这是该实例所属的类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接