Vấn đề với các giải pháp trên là nó sẽ không hoạt động để truy cập vào các biến lớp từ dụ biến:
print(Foo.number)
# 4
f = Foo()
print(f.number)
# 'Foo' object has no attribute 'number'
Hơn nữa, sử dụng metaclass rõ ràng không phải là quá tốt đẹp, như sử dụng trang trí thông thường property
.
Tôi đã cố gắng giải quyết vấn đề này. Dưới đây như thế nào nó hoạt động bây giờ:
@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
Như bạn thấy, chúng ta có @classproperty
mà làm việc cùng một cách như @property
cho các biến lớp. Điều duy nhất chúng tôi cần là trang trí lớp học bổ sung @classproperty_support
.
Giải pháp cũng hoạt động cho thuộc tính lớp chỉ đọc.
Dưới đây là thực hiện:
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
Lưu ý: mã không được thử nghiệm nhiều, cảm thấy tự do cần lưu ý nếu nó không hoạt động như bạn mong đợi.
Đây không phải là thuộc tính chỉ đọc. Các bộ mô tả Python cố ý không chặn các cuộc gọi cấp lớp tới 'setattr' và' delattr' bằng các phương thức '__set__' và' __delete__' của chúng. Vì vậy, 'Foo.number = 6' vẫn có thể hoạt động. – HardlyKnowEm