新类方法的__self__仍然引用以前的类?



我想做的似乎很奇怪,简而言之,我正在尝试构建一个新类并从另一个类复制实例/静态/类方法而不进行继承(即,我只想要一个只有几个方法的类)。当我在网上查找它时,似乎人们在建议我在插图(新类)中做了什么,但是当我试图测试它时,新类中声明的类方法似乎仍然指向以前的类(旧类)。

这是我所做的:


class OldClass(object):
current_count = 1
def __init__(self):
pass
@classmethod
def method_class(cls):
cls.current_count += 1
def method_normal(self):
return 2

# new class
class NewClass(object):
method_class_copy = OldClass.method_class
method_normal_copy = OldClass.method_normal

检查__self__会得到以下结果:

class_self = getattr(NewClass.method_class_copy, '__self__', None)
print(class_self)

它返回:

<class '__main__.OldClass'>

但是在实例化后检查实例方法:

instance_self = getattr(NewClass().method_normal_copy, '__self__', None)
print(instance_self)

如您所见,它仍然引用 OldClass,如果我NewClass.method_class_copy()运行类方法,OldClass.current_count将按照我定义的方式增加其值。

它返回:

<__main__.NewClass object at 0x000001D8CC1AEA88>
  • 如何使用类方法的__self__构造一个(新)类,该类实际上引用了NewClass而不是OldClass?
  • 为什么实例方法"自动调整"?

谢谢

这是描述符协议以及classmethod如何使用它的效果:classmethod通过查找它来绑定到它的类。粗略地说,查找some_cls.some_classmethod将预填充cls参数的方法返回到some_class

由于"普通"方法通过在实例上查找来绑定到它们的实例,因此在类上查找它们不会绑定它们。

>>> class Foo:
...    def some_normalmethod(self): ...
...    @classmethod
...    def some_classmethod(cls): ...
...
>>> Foo.some_classmethod
<bound method Foo.some_classmethod of <class '__main__.Foo'>>
>>> Foo.some_normalmethod
<function __main__.Foo.some_normalmethod(self)>

因此,通过从类中获取classmethod来"复制"它不会按预期工作(它已经绑定到类),而"复制"普通方法确实有效(它没有绑定到实例)。


为了"复制"classmethod,提取其底层功能并从中创建新classmethod

>>> bound_cm = Foo.some_classmethod  # bound classmethod
>>> base_cm = bound_cm.__func__      # function underlying classmethod
>>> class Bar:
...     some_classmethod = classmethod(base_cm)  # new classmethod of same function
...
>>> Bar.some_classmethod
<bound method Foo.some_classmethod of <class '__main__.Bar'>>

请注意,这将创建类方法的"副本",而不是基础函数。某些元数据,例如名称Foo.some_classmethod,仍然指向其来源。

如果需要原始classmethod对象,则绕过描述符协议将提供直接访问,而无需绑定方法。

>>> Foo.__dict__['some_classmethod']
<classmethod at 0x10b6d9d00>
>>> class Qux:
...     some_classmethod = Foo.__dict__['some_classmethod']
...
>>> Qux.some_classmethod
<bound method Foo.some_classmethod of <class '__main__.Qux'>>

只有在生成第二个类之后,才能通过将方法定义为类方法来执行所需的操作。

class OldClass(object):
current_count = 1
def __init__(self):
pass
def method_class(cls):
cls.current_count += 1

def method_normal(self):
return 2

# new class
class NewClass(object):
method_class_copy = OldClass.method_class
method_normal_copy = OldClass.method_normal

OldClass.method_class = classmethod(OldClass.method_class)
NewClass.method_class_copy = classmethod(NewClass.method_class_copy)

如果您对此进行测试

class_self = getattr(OldClass.method_class, '__self__', None)
print(class_self)
instance_self = getattr(OldClass().method_normal, '__self__', None)
print(instance_self)
print()
class_self = getattr(NewClass.method_class_copy, '__self__', None)
print(class_self)
instance_self = getattr(NewClass().method_normal_copy, '__self__', None)
print(instance_self)

你得到

<class '__main__.OldClass'>
<__main__.OldClass object at 0x7f1adc174ac0>
<class '__main__.NewClass'>
<__main__.NewClass object at 0x7f1adc138c70>

这就是你想要的。

编辑:您也可以动态执行此操作(在OldClass中使用@classmethod),首先使用staticmethod()使OldClass的所有类方法静态,然后创建NewClass然后调用classmethod()

最新更新