Python:类静态成员指向自身?
可能重复的问题:
我能在描述符的init方法中获取“拥有者”类的引用吗?
代码胜过千言万语:
>>> class ShortRib(object):
>>> def __init__(self, owner):
>>> self.owner = owner
>>>
>>> ... some more methods and stuff ...
>>>
>>>
>>> class Cow(object):
>>> shortRib = ShortRib(self)
>>>
>>>
>>> class BrownCow(Cow):
>>> pass
>>>
>>> BrownCow.shortRib.owner
<class '__main__.BrownCow'>
不过这并不奏效,虽然我希望它能工作。基本上,我想让每个类都有一些静态或类变量(我不太确定在这种情况下应该用哪个?),但我需要这些变量知道它们属于哪个类。不幸的是,我无法在类声明的主体中“获取”到这个类。当然,我可以使用装饰器来实现:
>>> def vars(**kwargs):
>>> def wrap(cls):
>>> for k, w in kwargs.items():
>>> setattr(cls, k, w(cls))
>>> return cls
>>> return wrap
>>>
>>> @vars(shortRib=lambda cls: ShortRib(cls)
>>> class BrownCow(Cow):
>>> ...
>>>
>>> BrownCow.shortRib.owner
这样是可以的。另一种方法是使用一个类装饰器,它会遍历所有的短肋骨和类似的静态变量,并在类声明完成后设置它们的拥有者。然而,这似乎是一种非常绕的、不直观的方式来完成一个应该很简单的操作:让类的静态成员知道它们属于哪个类。
有没有“正确”的方法来做到这一点?
澄清:
我希望这些成员属于类,而不是属于实例。我想采用一种几乎纯函数式的风格,只使用类来继承共享的行为,而根本不创建它们的实例。实例会让我的函数访问到任意的实例数据,这会破坏我想要的纯函数特性。我可以使用空的实例而不去碰它们,但我觉得使用纯类会更干净。
3 个回答
0
有两种方法可以实现你想要的效果:
- 你可以在任何类里面重写
__getattr__
方法,这样当你想要获取某个属性的值时,它就会返回你想要的任何东西。 - 你可以使用属性(property),它有一个获取器(getter),可以返回你希望返回的对象。
无论是 __getattr__
方法还是属性,都是可以被继承的。
1
为什么不让Cow类的实例拥有shortRibs,而是让类本身拥有呢?
class ShortRib(object):
def __init__(self,owner):
self.owner=owner
class Cow(object):
def __init__(self):
self.shortRib=ShortRib(self)
class BrownCow(Cow):
pass
print(BrownCow().shortRib.owner)
# <__main__.BrownCow object at 0xb76a8d6c>
(否则,你就需要一个类装饰器或者元类——就像你已经提到的那样。但简单的东西总比复杂的好,所以为什么不选择简单呢?)
顺便说一下,如果你真的想用类而不是实例:
class ShortRib(object):
def __init__(self, owner):
self.owner = owner
class MetaCow(type):
def __init__(cls, name, base, attrs):
super(MetaCow, cls).__init__(name, base, attrs)
cls.shortRib = ShortRib(cls)
class Cow(object):
__metaclass__ = MetaCow
class BrownCow(Cow):
pass
print(Cow.shortRib.owner)
# <class '__main__.Cow'>
print(BrownCow.shortRib.owner)
# <class '__main__.BrownCow'>
使用
class MetaCow(type):
def __new__(cls, name, base, attrs):
是不对的。type.__new__
的签名是
class MetaCow(type):
def __new__(meta, name, base, attrs):
因为你想修改的是cls
的属性,而不是meta
,所以应该使用MetaCow.__init__
而不是MetaCow.__new__
。
2
你可以很简单地在 __new__
里做到这一点:
class ShortRib(object):
def __init__(self, owner):
self.owner = owner
class Cow(object):
shortRib = None
def __new__(cls, *args, **kwargs):
if cls.shortRib == None:
cls.shortRib = ShortRib(cls)
return super(Cow, cls).__new__(cls, *args, **kwargs)
Cow()
Cow.shortRib.owner
如果你不介意引用 self.__class___
,也可以在 __init__
里实现。
你还可以通过元类来实现这个功能:
class ShortRib(object):
def __init__(self, owner):
self.owner = owner
class MetaCow(type):
def __new__(cls, name, base, attrs):
attrs['shortRib'] = ShortRib(cls)
return super(MetaCow, cls).__new__(cls, name, base, attrs)
class Cow(object):
__metaclass__ = MetaCow
Cow.shortRib.owner