Tôi muốn tạo một lớp bằng Python mà tôi có thể thêm và xóa các thuộc tính và phương thức. Làm thế nào tôi có thể biên tập điều đó?
Ồ, và xin đừng hỏi tại sao.
Tôi muốn tạo một lớp bằng Python mà tôi có thể thêm và xóa các thuộc tính và phương thức. Làm thế nào tôi có thể biên tập điều đó?
Ồ, và xin đừng hỏi tại sao.
Câu trả lời:
Tôi muốn tạo một lớp bằng Python mà tôi có thể thêm và xóa các thuộc tính và phương thức.
import types
class SpecialClass(object):
@classmethod
def removeVariable(cls, name):
return delattr(cls, name)
@classmethod
def addMethod(cls, func):
return setattr(cls, func.__name__, types.MethodType(func, cls))
def hello(self, n):
print n
instance = SpecialClass()
SpecialClass.addMethod(hello)
>>> SpecialClass.hello(5)
5
>>> instance.hello(6)
6
>>> SpecialClass.removeVariable("hello")
>>> instance.hello(7)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'SpecialClass' object has no attribute 'hello'
>>> SpecialClass.hello(8)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'SpecialClass' has no attribute 'hello'
Ví dụ này cho thấy sự khác biệt giữa việc thêm một phương thức vào một lớp và một thể hiện.
>>> class Dog():
... def __init__(self, name):
... self.name = name
...
>>> skip = Dog('Skip')
>>> spot = Dog('Spot')
>>> def talk(self):
... print 'Hi, my name is ' + self.name
...
>>> Dog.talk = talk # add method to class
>>> skip.talk()
Hi, my name is Skip
>>> spot.talk()
Hi, my name is Spot
>>> del Dog.talk # remove method from class
>>> skip.talk() # won't work anymore
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Dog instance has no attribute 'talk'
>>> import types
>>> f = types.MethodType(talk, skip, Dog)
>>> skip.talk = f # add method to specific instance
>>> skip.talk()
Hi, my name is Skip
>>> spot.talk() # won't work, since we only modified skip
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Dog instance has no attribute 'talk'
Một sự thay thế có thể thú vị để sử dụng types.MethodType
trong:
>>> f = types.MethodType(talk, puppy, Dog)
>>> puppy.talk = f # add method to specific instance
sẽ là khai thác thực tế rằng các hàm là bộ mô tả :
>>> puppy.talk = talk.__get__(puppy, Dog)
Tôi muốn tạo một lớp bằng Python mà tôi có thể thêm và xóa các thuộc tính và phương thức. Làm thế nào tôi có thể biên tập điều đó?
Bạn có thể thêm và xóa các thuộc tính và phương thức cho bất kỳ lớp nào và chúng sẽ có sẵn cho tất cả các phiên bản của lớp:
>>> def method1(self):
pass
>>> def method1(self):
print "method1"
>>> def method2(self):
print "method2"
>>> class C():
pass
>>> c = C()
>>> c.method()
Traceback (most recent call last):
File "<pyshell#62>", line 1, in <module>
c.method()
AttributeError: C instance has no attribute 'method'
>>> C.method = method1
>>> c.method()
method1
>>> C.method = method2
>>> c.method()
method2
>>> del C.method
>>> c.method()
Traceback (most recent call last):
File "<pyshell#68>", line 1, in <module>
c.method()
AttributeError: C instance has no attribute 'method'
>>> C.attribute = "foo"
>>> c.attribute
'foo'
>>> c.attribute = "bar"
>>> c.attribute
'bar'
bạn có thể chỉ định trực tiếp cho lớp (bằng cách truy cập vào tên lớp ban đầu hoặc thông qua __class__
):
class a : pass
ob=a()
ob.__class__.blah=lambda self,k: (3, self,k)
ob.blah(5)
ob2=a()
ob2.blah(7)
sẽ in
(3, <__main__.a instance at 0x7f18e3c345f0>, 5)
(3, <__main__.a instance at 0x7f18e3c344d0>, 7)
một thay thế khác, nếu bạn cần thay thế bán buôn lớp là sửa đổi thuộc tính lớp :
>>> class A(object):
... def foo(self):
... print 'A'
...
>>> class B(object):
... def foo(self):
... print 'Bar'
...
>>> a = A()
>>> a.foo()
A
>>> a.__class__ = B
>>> a.foo()
Bar
Đơn giản:
f1 = lambda:0 #method for instances
f2 = lambda _:0 #method for class
class C: pass #class
c1,c2 = C(),C() #instances
print dir(c1),dir(c2)
#add to the Instances
c1.func = f1
c1.any = 1.23
print dir(c1),dir(c2)
print c1.func(),c1.any
del c1.func,c1.any
#add to the Class
C.func = f2
C.any = 1.23
print dir(c1),dir(c2)
print c1.func(),c1.any
print c2.func(),c2.any
kết quả là:
['__doc__', '__module__'] ['__doc__', '__module__']
['__doc__', '__module__', 'any', 'func'] ['__doc__', '__module__']
0 1.23
['__doc__', '__module__', 'any', 'func'] ['__doc__', '__module__', 'any', 'func']
0 1.23
0 1.23
Bản thân lớp có nhất thiết phải sửa đổi không? Hay mục tiêu chỉ đơn giản là thay thế đối tượng.method () nào thực hiện tại một điểm cụ thể trong thời gian chạy?
Tôi hỏi vì tôi đã tránh được vấn đề thực sự sửa đổi lớp thành các cuộc gọi phương thức cụ thể vá khỉ trong khuôn khổ của tôi với getattribute và Runtime Decorator trên đối tượng kế thừa Cơ sở của tôi.
Các phương thức được truy xuất bởi một đối tượng Cơ sở trong getattribute được bao bọc trong một Runtime_Decorator để phân tích cú pháp phương thức gọi các đối số từ khóa cho các trình trang trí / các bản vá khỉ áp dụng.
Điều này cho phép bạn sử dụng cú pháp object.method (Monkey_patch = "mypatch"), object.method (decorator = "mydecorator") và thậm chí object.method (decorators = my_decorator_list).
Điều này hoạt động cho bất kỳ lệnh gọi phương thức riêng lẻ nào (tôi bỏ qua các phương thức ma thuật), làm như vậy mà không thực sự sửa đổi bất kỳ thuộc tính lớp / phiên bản nào, có thể sử dụng các phương thức tùy ý, thậm chí là ngoại lai để vá và sẽ hoạt động minh bạch trên các lớp con kế thừa từ Base (miễn là chúng không tất nhiên không ghi đè getattribute ).
import trace
def monkey_patched(self, *args, **kwargs):
print self, "Tried to call a method, but it was monkey patched instead"
return "and now for something completely different"
class Base(object):
def __init__(self):
super(Base, self).__init__()
def testmethod(self):
print "%s test method" % self
def __getattribute__(self, attribute):
value = super(Base, self).__getattribute__(attribute)
if "__" not in attribute and callable(value):
value = Runtime_Decorator(value)
return value
class Runtime_Decorator(object):
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
if kwargs.has_key("monkey_patch"):
module_name, patch_name = self._resolve_string(kwargs.pop("monkey_patch"))
module = self._get_module(module_name)
monkey_patch = getattr(module, patch_name)
return monkey_patch(self.function.im_self, *args, **kwargs)
if kwargs.has_key('decorator'):
decorator_type = str(kwargs['decorator'])
module_name, decorator_name = self._resolve_string(decorator_type)
decorator = self._get_decorator(decorator_name, module_name)
wrapped_function = decorator(self.function)
del kwargs['decorator']
return wrapped_function(*args, **kwargs)
elif kwargs.has_key('decorators'):
decorators = []
for item in kwargs['decorators']:
module_name, decorator_name = self._resolve_string(item)
decorator = self._get_decorator(decorator_name, module_name)
decorators.append(decorator)
wrapped_function = self.function
for item in reversed(decorators):
wrapped_function = item(wrapped_function)
del kwargs['decorators']
return wrapped_function(*args, **kwargs)
else:
return self.function(*args, **kwargs)
def _resolve_string(self, string):
try: # attempt to split the string into a module and attribute
module_name, decorator_name = string.split(".")
except ValueError: # there was no ".", it's just a single attribute
module_name = "__main__"
decorator_name = string
finally:
return module_name, decorator_name
def _get_module(self, module_name):
try: # attempt to load the module if it exists already
module = modules[module_name]
except KeyError: # import it if it doesn't
module = __import__(module_name)
finally:
return module
def _get_decorator(self, decorator_name, module_name):
module = self._get_module(module_name)
try: # attempt to procure the decorator class
decorator_wrap = getattr(module, decorator_name)
except AttributeError: # decorator not found in module
print("failed to locate decorators %s for function %s." %\
(kwargs["decorator"], self.function))
else:
return decorator_wrap # instantiate the class with self.function
class Tracer(object):
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
tracer = trace.Trace(trace=1)
tracer.runfunc(self.function, *args, **kwargs)
b = Base()
b.testmethod(monkey_patch="monkey_patched")
b.testmethod(decorator="Tracer")
#b.testmethod(monkey_patch="external_module.my_patch")
Nhược điểm của phương pháp này là getattribute móc tất cả quyền truy cập vào các thuộc tính, vì vậy việc kiểm tra và gói phương thức tiềm năng xảy ra ngay cả đối với các thuộc tính không phải là phương thức + sẽ không sử dụng tính năng cho lệnh gọi cụ thể được đề cập. Và việc sử dụng getattribute vốn dĩ hơi phức tạp.
Tác động thực tế của chi phí này trong trải nghiệm của tôi / cho mục đích của tôi là không đáng kể và máy của tôi chạy Celeron lõi kép. Việc triển khai trước đó, tôi đã sử dụng các phương thức nội quan dựa trên đối tượng init và sau đó ràng buộc Runtime_Decorator với các phương thức. Làm những việc theo cách đó đã loại bỏ nhu cầu sử dụng getattribute và giảm chi phí đã đề cập trước đây ... tuy nhiên, nó cũng phá vỡ dây dưa (có thể không thì là) và kém năng động hơn so với cách tiếp cận này.
Các trường hợp sử dụng duy nhất mà tôi thực sự gặp phải "trong tự nhiên" với kỹ thuật này là tính thời gian và truy tìm trang trí. Tuy nhiên, khả năng nó mở ra là vô cùng rộng lớn.
Nếu bạn có một lớp tồn tại trước đó không thể được thực hiện để kế thừa từ một cơ sở khác (hoặc sử dụng kỹ thuật định nghĩa lớp của chính nó hoặc trong lớp cơ sở của nó '), thì rất tiếc toàn bộ điều không áp dụng cho vấn đề của bạn.
Tôi không nghĩ rằng việc thiết lập / xóa các thuộc tính không thể gọi trên một lớp trong thời gian chạy có nhất thiết phải là một thách thức như vậy không? trừ khi bạn muốn các lớp kế thừa từ lớp đã sửa đổi cũng tự động phản ánh những thay đổi trong chính chúng ... Mặc dù vậy, đó sẽ là một con sâu 'nother can o' hoàn toàn bởi âm thanh của nó.