Tất nhiên rằng metaclasses là cách pythonic nhất để đi khi bạn muốn thay đổi đường đi python đó tạo ra các đối tượng. Có thể thực hiện bằng cách ghi đè phương thức __new__
của lớp học của bạn. Nhưng có một số điểm xung quanh vấn đề này (đặc biệt cho python 3.X) mà tôi muốn đề cập đến:
types.FunctionType
không bảo vệ các phương pháp đặc biệt được trang trí, vì chúng là các loại chức năng. Như một cách tổng quát hơn, bạn chỉ có thể trang trí các đối tượng mà tên của chúng không được bắt đầu bằng dấu gạch dưới kép (__
). Một lợi ích khác của phương pháp này là nó cũng bao gồm những đối tượng mà tồn tại trong không gian tên và bắt đầu với __
nhưng không có chức năng như __qualname__
, __module__
vv
Các namespace
lập luận trong phần đầu __new__
's không chứa các thuộc tính lớp trong số __init__
. Lý do là __new__
thực thi trước __init__
(khởi tạo).
Không cần sử dụng classmethod
làm trang trí, như trong hầu hết các lần bạn nhập trang trí của mình từ mô-đun khác.
- Nếu lớp học của bạn chứa mục toàn cầu (bên cạnh
__init__
) vì không được trang trí cùng với việc kiểm tra xem tên có bắt đầu bằng __
hay không, bạn có thể kiểm tra loại với types.FunctionType
để đảm bảo rằng bạn không trang trí một đối tượng không có chức năng.
Đây là một metacalss mẫu mà bạn có thể sử dụng:
class TheMeta(type):
def __new__(cls, name, bases, namespace, **kwds):
# if your decorator is a class method of the metaclass use
# `my_decorator = cls.my_decorator` in order to invoke the decorator.
namespace = {k: v if k.startswith('__') else my_decorator(v) for k, v in namespace.items()}
return type.__new__(cls, name, bases, namespace)
Demo:
def my_decorator(func):
def wrapper(self, arg):
# You can also use *args instead of (self, arg) and pass the *args
# to the function in following call.
return "the value {} gets modified!!".format(func(self, arg))
return wrapper
class TheMeta(type):
def __new__(cls, name, bases, namespace, **kwds):
# my_decorator = cls.my_decorator (if the decorator is a classmethod)
namespace = {k: v if k.startswith('__') else my_decorator(v) for k, v in namespace.items()}
return type.__new__(cls, name, bases, namespace)
class MyClass(metaclass=TheMeta):
# a = 10
def __init__(self, *args, **kwargs):
self.item = args[0]
self.value = kwargs['value']
def __getattr__(self, attr):
return "This class hasn't provide the attribute {}.".format(attr)
def myfunction_1(self, arg):
return arg ** 2
def myfunction_2(self, arg):
return arg ** 3
myinstance = MyClass(1, 2, value=100)
print(myinstance.myfunction_1(5))
print(myinstance.myfunction_2(2))
print(myinstance.item)
print(myinstance.p)
Output:
the value 25 gets modified!!
the value 8 gets modified!!
1
This class hasn't provide the attribute p. # special method is not decorated.
Đối với kiểm tra i 3rd tem từ các ghi chú nói trên, bạn có thể bỏ ghi chú dòng a = 10
và làm print(myinstance.a)
và xem kết quả sau đó thay đổi sự hiểu biết từ điển trong __new__
như sau sau đó xem kết quả một lần nữa:
namespace = {k: v if k.startswith('__') and not isinstance(v, types.FunctionType)\
else my_decorator(v) for k, v in namespace.items()}
Tôi hoàn toàn đồng ý, tôi cũng thoải mái hơn nhiều với trang trí rõ ràng của mọi chức năng hơn là một trang trí ngầm. Chỉ tò mò là tất cả. – dochead