Các thuộc tính lớp có thể được đọc trên lớp hoặc một thể hiện, nhưng bạn chỉ có thể đặt chúng trên lớp [cố gắng đặt chúng trên một thể hiện sẽ chỉ tạo một thuộc tính thể hiện sẽ theo dõi thuộc tính lớp].
Nếu điều kiện được biết đến tại thời điểm nhập khẩu, bạn chỉ có thể kiểm tra nó trong cơ thể class
:
xxx = True
class A[object]:
cls_attr = 'value'
class B[A]:
if xxx:
cls_attr = 'this_value'
else
cls_attr = 'that_value'
Bây giờ nếu bạn muốn thay đổi nó trong quá trình thực hiện chương trình, bạn phải sử dụng classmethod
:
class B[A]:
@classmethod
def set_cls_attr[cls, xxx]:
if xxx:
cls.cls_attr = 'this_value'
else:
cls.cls_attr = 'that_value'
Hoặc nếu bạn cần truy cập phiên bản của mình trong quá trình kiểm tra:
class B[A]:
def set_cls_attr[self, xxx]:
cls = type[self]
if xxx:
cls.cls_attr = 'this_value'
else:
cls.cls_attr = 'that_value'
Logo Python 2021/06/05 - Chỉnh sửa:
Tôi đã viết bài viết này vào năm ngoái, và kể từ khi đã tìm hiểu thêm về cách Python hoạt động trong nội bộ. Các giải pháp dưới đây thực sự hoạt động, nhưng khá không hiệu quả.
Các chức năng của GetAttr, SetAttr, Hasattr rất tuyệt vời và có thời gian và địa điểm được sử dụng, nhưng chúng yêu cầu tra cứu đối tượng đó trước khi truy cập/kiểm tra thuộc tính.
Tuy nhiên, được coi là lập trình xấu, các lớp mở giống như Ruby/JavaScript [nghĩa là các lớp có thể được sửa đổi trong thời gian chạy] thực sự có thể là một mẫu lập trình khi phát triển kiến trúc trình cắm. Và, đoán xem, Python cũng có chức năng đó. Mã này cho phép thêm các thuộc tính [thông thường các phương thức] vào các lớp, ngay cả khi đã được khởi tạo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | ''' Created on Mar 31, 2011 @author: Nabil Stendardo ''' def noop[*args,**kwargs]: pass def modify_class[cls,name=None,default=noop]: """ Modify an class attribute/method. This decorator factory returns a decorator which modifies on the fly [i.e. monkeypatches] the class "cls" by adding or replacing the attribute [typically method] indicated by the variable "name" [which defaults to the name of the wrapper function] with the result of the decorated function, to which is passed as only parameter the old attribute with the same name [or a default value, by default a dummy function, if none exists]. @param cls: the class to modify @param name: the name of the attribute/method to modify in the class [defaults to the decorated function name] @param default: the value which is passed to the decorated function if there is no attribute named 'name' in 'cls' [defaults to a dummy function] @return: a decorator """ def wrapper[fn]: """ The actual decorator returned by modify_class, which actually modifies the class. @param fn: the function to decorate @return: the argument function. """ if name is None: name_ = fn.__name__ else: name_ = name original_method = getattr[cls,name_,default] new_method = fn[original_method] setattr[cls,name_,new_method] return fn return wrapper ##TESTING CODE import unittest2 class TestModifyClass[unittest2.TestCase]: def test_modifyClass[self]: class Foo[object]: def baz[self]: return 0 def bar[self,argument]: return argument**2 foo = Foo[] # Note: the foo object is instantiated BEFORE the class modification @modify_class[Foo] def baz[parent]: # Note: Different signature def baz[self,argument]: return self.bar[argument] return baz @modify_class[Foo] def bar[parent]: def bar[self,argument]: return parent[self,argument] * 3 return bar @modify_class[Foo,'bar'] # Note: Different wrapper name def toto[parent]: def bar[self,argument]: return parent[self,argument] + 1 return bar for i in range[25]: self.assertEqual[foo.baz[i],[i**2]*3+1] if __name__ == '__main__': unittest2.main[] |