Có thể sử dụng hàm property () với các hàm trang trí classmethod không?
Không.
Tuy nhiên, một classmethod chỉ đơn giản là một phương thức ràng buộc (một hàm một phần) trên một lớp có thể truy cập được từ các thể hiện của lớp đó.
Vì cá thể là một chức năng của lớp và bạn có thể lấy được lớp từ cá thể đó, nên bạn có thể nhận bất kỳ hành vi mong muốn nào bạn có thể muốn từ một thuộc tính lớp với property
:
class Example(object):
_class_property = None
@property
def class_property(self):
return self._class_property
@class_property.setter
def class_property(self, value):
type(self)._class_property = value
@class_property.deleter
def class_property(self):
del type(self)._class_property
Mã này có thể được sử dụng để kiểm tra - nó sẽ vượt qua mà không gây ra bất kỳ lỗi nào:
ex1 = Example()
ex2 = Example()
ex1.class_property = None
ex2.class_property = 'Example'
assert ex1.class_property is ex2.class_property
del ex2.class_property
assert not hasattr(ex1, 'class_property')
Và lưu ý rằng chúng tôi hoàn toàn không cần siêu dữ liệu - và bạn không truy cập trực tiếp vào siêu dữ liệu thông qua các trường hợp của lớp.
viết một @classproperty
trang trí
Bạn thực sự có thể tạo một trình classproperty
trang trí chỉ trong một vài dòng mã bằng cách phân lớp property
(nó được triển khai trong C, nhưng bạn có thể thấy Python tương đương ở đây ):
class classproperty(property):
def __get__(self, obj, objtype=None):
return super(classproperty, self).__get__(objtype)
def __set__(self, obj, value):
super(classproperty, self).__set__(type(obj), value)
def __delete__(self, obj):
super(classproperty, self).__delete__(type(obj))
Sau đó đối xử với trình trang trí như thể nó là một phân loại kết hợp với thuộc tính:
class Foo(object):
_bar = 5
@classproperty
def bar(cls):
"""this is the bar attribute - each subclass of Foo gets its own.
Lookups should follow the method resolution order.
"""
return cls._bar
@bar.setter
def bar(cls, value):
cls._bar = value
@bar.deleter
def bar(cls):
del cls._bar
Và mã này sẽ hoạt động mà không có lỗi:
def main():
f = Foo()
print(f.bar)
f.bar = 4
print(f.bar)
del f.bar
try:
f.bar
except AttributeError:
pass
else:
raise RuntimeError('f.bar must have worked - inconceivable!')
help(f) # includes the Foo.bar help.
f.bar = 5
class Bar(Foo):
"a subclass of Foo, nothing more"
help(Bar) # includes the Foo.bar help!
b = Bar()
b.bar = 'baz'
print(b.bar) # prints baz
del b.bar
print(b.bar) # prints 5 - looked up from Foo!
if __name__ == '__main__':
main()
Nhưng tôi không chắc điều này sẽ được khuyên như thế nào. Một bài viết danh sách gửi thư cũ cho thấy nó không nên làm việc.
Bắt tài sản để làm việc trên lớp:
Nhược điểm ở trên là "thuộc tính lớp" không thể truy cập được từ lớp, bởi vì nó chỉ đơn giản sẽ ghi đè lên bộ mô tả dữ liệu từ lớp __dict__
.
Tuy nhiên, chúng ta có thể ghi đè lên điều này bằng một thuộc tính được xác định trong siêu dữ liệu __dict__
. Ví dụ:
class MetaWithFooClassProperty(type):
@property
def foo(cls):
"""The foo property is a function of the class -
in this case, the trivial case of the identity function.
"""
return cls
Và sau đó, một thể hiện của lớp siêu dữ liệu có thể có một thuộc tính truy cập vào thuộc tính của lớp bằng cách sử dụng nguyên tắc đã được trình bày trong các phần trước:
class FooClassProperty(metaclass=MetaWithFooClassProperty):
@property
def foo(self):
"""access the class's property"""
return type(self).foo
Và bây giờ chúng ta thấy cả hai ví dụ
>>> FooClassProperty().foo
<class '__main__.FooClassProperty'>
và lớp học
>>> FooClassProperty.foo
<class '__main__.FooClassProperty'>
có quyền truy cập vào tài sản lớp.