Bjö*_*lex 66 python properties class
基本上我想做这样的事情:
class foo:
x = 4
@property
@classmethod
def number(cls):
return x
Run Code Online (Sandbox Code Playgroud)
然后我希望以下工作:
>>> foo.number
4
Run Code Online (Sandbox Code Playgroud)
不幸的是,上述方法无效.而不是给我4
它给我<property object at 0x101786c58>
.有没有办法实现上述目标?
unu*_*tbu 65
这将创建Foo.number
一个只读属性:
class MetaFoo(type):
@property
def number(cls):
return cls.x
class Foo(object, metaclass=MetaFoo):
x = 4
print(Foo.number)
# 4
Foo.number = 6
# AttributeError: can't set attribute
Run Code Online (Sandbox Code Playgroud)
说明:使用@property时的常见情况如下所示:
class Foo(object):
@property
def number(self):
...
foo = Foo()
Run Code Online (Sandbox Code Playgroud)
定义的属性Foo
相对于其实例是只读的.那就是,foo.number = 6
会提高一个AttributeError
.
类似地,如果你想Foo.number
提出一个,AttributeError
你需要设置一个定义的属性type(Foo)
.因此需要一个元类.
请注意,这种只读不受黑客的影响.通过更改Foo的类可以使该属性可写:
class Base(type): pass
Foo.__class__ = Base
# makes Foo.number a normal class attribute
Foo.number = 6
print(Foo.number)
Run Code Online (Sandbox Code Playgroud)
版画
6
Run Code Online (Sandbox Code Playgroud)
或者,如果你想建立Foo.number
一个可设置的财产,
class WritableMetaFoo(type):
@property
def number(cls):
return cls.x
@number.setter
def number(cls, value):
cls.x = value
Foo.__class__ = WritableMetaFoo
# Now the assignment modifies `Foo.x`
Foo.number = 6
print(Foo.number)
Run Code Online (Sandbox Code Playgroud)
还打印6.
bob*_*nce 45
在property
从一类访问时描述符总是返回本身(即当instance
是None
在它的__get__
方法).
如果这不是您想要的,您可以编写一个始终使用类object(owner
)而不是实例的新描述符:
>>> class classproperty(object):
... def __init__(self, getter):
... self.getter= getter
... def __get__(self, instance, owner):
... return self.getter(owner)
...
>>> class Foo(object):
... x= 4
... @classproperty
... def number(cls):
... return cls.x
...
>>> Foo().number
4
>>> Foo.number
4
Run Code Online (Sandbox Code Playgroud)
Vik*_*aag 16
我同意unubtu的回答 ; 它似乎工作,然而,它不适用于Python 3上的这种精确语法(具体来说,Python 3.4是我所挣扎的).以下是在Python 3.4下必须形成模式以使事情有效的方法,似乎:
class MetaFoo(type):
@property
def number(cls):
return cls.x
class Foo(metaclass=MetaFoo):
x = 4
print(Foo.number)
# 4
Foo.number = 6
# AttributeError: can't set attribute
Run Code Online (Sandbox Code Playgroud)
Mikhail Gerasimov的解决方案非常完整。不幸的是,这是一个缺点。如果您在使用他的classproperty有一类,没有子类可以使用它因
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
有class Wrapper
。
幸运的是,可以解决此问题。创建时,只需从给定类的元类继承即可class Meta
。
def classproperty_support(cls):
"""
Class decorator to add metaclass to our class.
Metaclass uses to add descriptors to class attributes, see:
http://stackoverflow.com/a/26634248/1113207
"""
# Use type(cls) to use metaclass of given class
class Meta(type(cls)):
pass
for name, obj in vars(cls).items():
if isinstance(obj, classproperty):
setattr(Meta, name, property(obj.fget, obj.fset, obj.fdel))
class Wrapper(cls, metaclass=Meta):
pass
return Wrapper
Run Code Online (Sandbox Code Playgroud)
上面解决方案的问题是,它无法从实例变量访问类变量:
print(Foo.number)
# 4
f = Foo()
print(f.number)
# 'Foo' object has no attribute 'number'
Run Code Online (Sandbox Code Playgroud)
而且,显式使用元类并不像使用常规property
装饰器那样好。
我试图解决这个问题。现在,它是如何工作的:
@classproperty_support
class Bar(object):
_bar = 1
@classproperty
def bar(cls):
return cls._bar
@bar.setter
def bar(cls, value):
cls._bar = value
# @classproperty should act like regular class variable.
# Asserts can be tested with it.
# class Bar:
# bar = 1
assert Bar.bar == 1
Bar.bar = 2
assert Bar.bar == 2
foo = Bar()
baz = Bar()
assert foo.bar == 2
assert baz.bar == 2
Bar.bar = 50
assert baz.bar == 50
assert foo.bar == 50
Run Code Online (Sandbox Code Playgroud)
如您所见,我们的@classproperty
工作方式与@property
类变量相同。我们唯一需要的是附加的@classproperty_support
类装饰器。
解决方案也适用于只读类属性。
这是实现:
class classproperty:
"""
Same as property(), but passes obj.__class__ instead of obj to fget/fset/fdel.
Original code for property emulation:
https://docs.python.org/3.5/howto/descriptor.html#properties
"""
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
if doc is None and fget is not None:
doc = fget.__doc__
self.__doc__ = doc
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj.__class__)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj.__class__, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj.__class__)
def getter(self, fget):
return type(self)(fget, self.fset, self.fdel, self.__doc__)
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.__doc__)
def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel, self.__doc__)
def classproperty_support(cls):
"""
Class decorator to add metaclass to our class.
Metaclass uses to add descriptors to class attributes, see:
http://stackoverflow.com/a/26634248/1113207
"""
class Meta(type):
pass
for name, obj in vars(cls).items():
if isinstance(obj, classproperty):
setattr(Meta, name, property(obj.fget, obj.fset, obj.fdel))
class Wrapper(cls, metaclass=Meta):
pass
return Wrapper
Run Code Online (Sandbox Code Playgroud)
注意:代码没有经过太多测试,请注意它是否无法按预期工作。
归档时间: |
|
查看次数: |
25967 次 |
最近记录: |