创建一个既是 class 方法又是实例方法的方法
Make a method that is both a class method and an instance method
有没有办法在Python中定义一个既是class方法又是实例方法的方法,这样cls
和self
都是接收者.特别是,我希望制作一种方法,(1) 知道在 class (Foo.method(value)
) 上调用时调用了哪个 class 并且 (2) 知道它是哪个实例在实例上调用时调用 (foo.method()
)。
例如,我设想的工作方式如下:
class Foo:
def __str__(self): return 'Foo()'
@classinstancemethod
def method(cls, self):
print(cls, self)
class Bar(Foo):
def __str__(self): return 'Bar()'
Foo().method() # <class '__main__.Foo'> Foo()
Bar().method() # <class '__main__.Bar'> Bar()
Foo.method(Foo()) # <class '__main__.Foo'> Foo()
Foo.method(Bar()) # <class '__main__.Foo'> Bar()
Bar.method(Foo()) # <class '__main__.Bar'> Foo()
请注意,我知道可以像 Foo.foo(value)
那样调用未修饰的方法,但这不是我想要的,因为它没有获得 cls
变量。如果没有 cls
变量,该方法不知道它刚刚调用了哪个 class。它可能被称为 Bar.method(value)
,现在可以知道 value
是否是 Foo
的实例。未修饰的方法更像是静态方法和实例方法,而不是 class 方法和实例方法。
您不需要装饰器。这就是方法已经起作用的方式;实例作为第一个参数传递——当从实例调用方法时隐式传递,从 class 显式传递——在实例可用的情况下,您可以通过在实例上调用 type
来检索 class:
class Foo(object):
def __repr__(self): return 'Foo()'
def method(self):
print((type(self), self))
class Bar(Foo):
def __repr__(self): return 'Bar()'
另外注意,在__str__
(或__repr__
)特殊方法中,你应该return一个字符串,而不是打印。
我使用了 __repr__
,因为在容器对象(此处为元组)内部时,不会调用 __str__
来打印实例。
更新:
考虑到上述方法中 class/instance 打印的问题,您可以改用描述符来正确管理方法中正确的 class 和实例选择:
class classinstancemethod():
def __get__(self, obj, cls):
def method(inst=None):
print(cls, inst if inst else obj)
return method
class Foo(object):
method = classinstancemethod()
def __str__(self): return 'Foo()'
class Bar(Foo):
def __str__(self): return 'Bar()'
这可以通过将 classinstancemethod
作为自定义 descriptor 实现来解决。
简而言之,描述符必须定义一个 __get__
方法,该方法将在作为属性访问时调用(如 Foo.method
或 Foo().method
)。此方法将传递实例和 class 作为参数和 returns 一个 绑定 方法(即它 returns 一个带有 cls
和 self
参数)。调用此绑定方法时,它会将内置 cls
和 self
参数转发给实际方法。
class classinstancemethod:
def __init__(self, method, instance=None, owner=None):
self.method = method
self.instance = instance
self.owner = owner
def __get__(self, instance, owner=None):
return type(self)(self.method, instance, owner)
def __call__(self, *args, **kwargs):
instance = self.instance
if instance is None:
if not args:
raise TypeError('missing required parameter "self"')
instance, args = args[0], args[1:]
cls = self.owner
return self.method(cls, instance, *args, **kwargs)
结果:
class Foo:
def __repr__(self): return 'Foo()'
@classinstancemethod
def method(cls, self):
print((cls, self))
class Bar(Foo):
def __repr__(self): return 'Bar()'
Foo().method() # (<class '__main__.Foo'>, 'Foo()')
Bar().method() # (<class '__main__.Bar'>, 'Bar()')
Foo.method(Foo()) # (<class '__main__.Foo'>, 'Foo()')
Foo.method(Bar()) # (<class '__main__.Foo'>, 'Bar()')
Bar.method(Foo()) # (<class '__main__.Bar'>, 'Foo()')
有没有办法在Python中定义一个既是class方法又是实例方法的方法,这样cls
和self
都是接收者.特别是,我希望制作一种方法,(1) 知道在 class (Foo.method(value)
) 上调用时调用了哪个 class 并且 (2) 知道它是哪个实例在实例上调用时调用 (foo.method()
)。
例如,我设想的工作方式如下:
class Foo:
def __str__(self): return 'Foo()'
@classinstancemethod
def method(cls, self):
print(cls, self)
class Bar(Foo):
def __str__(self): return 'Bar()'
Foo().method() # <class '__main__.Foo'> Foo()
Bar().method() # <class '__main__.Bar'> Bar()
Foo.method(Foo()) # <class '__main__.Foo'> Foo()
Foo.method(Bar()) # <class '__main__.Foo'> Bar()
Bar.method(Foo()) # <class '__main__.Bar'> Foo()
请注意,我知道可以像 Foo.foo(value)
那样调用未修饰的方法,但这不是我想要的,因为它没有获得 cls
变量。如果没有 cls
变量,该方法不知道它刚刚调用了哪个 class。它可能被称为 Bar.method(value)
,现在可以知道 value
是否是 Foo
的实例。未修饰的方法更像是静态方法和实例方法,而不是 class 方法和实例方法。
您不需要装饰器。这就是方法已经起作用的方式;实例作为第一个参数传递——当从实例调用方法时隐式传递,从 class 显式传递——在实例可用的情况下,您可以通过在实例上调用 type
来检索 class:
class Foo(object):
def __repr__(self): return 'Foo()'
def method(self):
print((type(self), self))
class Bar(Foo):
def __repr__(self): return 'Bar()'
另外注意,在__str__
(或__repr__
)特殊方法中,你应该return一个字符串,而不是打印。
我使用了 __repr__
,因为在容器对象(此处为元组)内部时,不会调用 __str__
来打印实例。
更新:
考虑到上述方法中 class/instance 打印的问题,您可以改用描述符来正确管理方法中正确的 class 和实例选择:
class classinstancemethod():
def __get__(self, obj, cls):
def method(inst=None):
print(cls, inst if inst else obj)
return method
class Foo(object):
method = classinstancemethod()
def __str__(self): return 'Foo()'
class Bar(Foo):
def __str__(self): return 'Bar()'
这可以通过将 classinstancemethod
作为自定义 descriptor 实现来解决。
简而言之,描述符必须定义一个 __get__
方法,该方法将在作为属性访问时调用(如 Foo.method
或 Foo().method
)。此方法将传递实例和 class 作为参数和 returns 一个 绑定 方法(即它 returns 一个带有 cls
和 self
参数)。调用此绑定方法时,它会将内置 cls
和 self
参数转发给实际方法。
class classinstancemethod:
def __init__(self, method, instance=None, owner=None):
self.method = method
self.instance = instance
self.owner = owner
def __get__(self, instance, owner=None):
return type(self)(self.method, instance, owner)
def __call__(self, *args, **kwargs):
instance = self.instance
if instance is None:
if not args:
raise TypeError('missing required parameter "self"')
instance, args = args[0], args[1:]
cls = self.owner
return self.method(cls, instance, *args, **kwargs)
结果:
class Foo:
def __repr__(self): return 'Foo()'
@classinstancemethod
def method(cls, self):
print((cls, self))
class Bar(Foo):
def __repr__(self): return 'Bar()'
Foo().method() # (<class '__main__.Foo'>, 'Foo()')
Bar().method() # (<class '__main__.Bar'>, 'Bar()')
Foo.method(Foo()) # (<class '__main__.Foo'>, 'Foo()')
Foo.method(Bar()) # (<class '__main__.Foo'>, 'Bar()')
Bar.method(Foo()) # (<class '__main__.Bar'>, 'Foo()')