Ich möchte eine Klasse in Python erstellen, mit der ich Attribute und Methoden hinzufügen und entfernen kann. Wie kann ich das erreichen?
Oh, und bitte frag nicht warum.
python
reflection
runtime
Migol
quelle
quelle
Antworten:
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'
quelle
Dieses Beispiel zeigt die Unterschiede zwischen dem Hinzufügen einer Methode zu einer Klasse und einer Instanz.
>>> 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'
quelle
Eine möglicherweise interessante Alternative zur Verwendung
types.MethodType
in:>>> f = types.MethodType(talk, puppy, Dog) >>> puppy.talk = f # add method to specific instance
wäre, die Tatsache auszunutzen, dass Funktionen Deskriptoren sind :
>>> puppy.talk = talk.__get__(puppy, Dog)
quelle
Sie können jeder Klasse Attribute und Methoden hinzufügen und daraus entfernen, die allen Instanzen der Klasse zur Verfügung stehen:
>>> 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'
quelle
Sie können der Klasse einfach direkt zuweisen (entweder durch Zugriff auf den ursprünglichen Klassennamen oder über
__class__
):class a : pass ob=a() ob.__class__.blah=lambda self,k: (3, self,k) ob.blah(5) ob2=a() ob2.blah(7)
wird gedruckt
(3, <__main__.a instance at 0x7f18e3c345f0>, 5) (3, <__main__.a instance at 0x7f18e3c344d0>, 7)
quelle
eine weitere Alternative, wenn Sie die Klasse Großhandel ersetzen müssen , ist das zu ändern Klasse Attribut:
>>> 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
quelle
Einfach:
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
was in ... endet:
['__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
quelle
Muss die Klasse selbst unbedingt geändert werden? Oder ist das Ziel einfach zu ersetzen, was object.method () zu einem bestimmten Zeitpunkt zur Laufzeit tut?
Ich frage, weil ich das Problem, die Klasse tatsächlich zu modifizieren, um Affen-Patch-spezifische Methodenaufrufe in meinem Framework mit getattribute und einem Runtime Decorator auf meinem Base-Vererbungsobjekt zu ändern, umgehe .
Methoden, die von einem Basisobjekt in getattribute abgerufen werden, werden in einen Runtime_Decorator eingeschlossen, der die Methode analysiert und Schlüsselwortargumente aufruft, damit Dekoratoren / Affen-Patches angewendet werden.
Auf diese Weise können Sie die Syntax object.method (monkey_patch = "mypatch"), object.method (decorator = "mydecorator") und sogar object.method (decorators = my_decorator_list) verwenden.
Dies funktioniert für jeden einzelnen Methodenaufruf (ich lasse magische Methoden weg), ohne Klassen- / Instanzattribute zu ändern, kann beliebige, sogar fremde Methoden zum Patchen verwenden und funktioniert transparent bei Unterklassen, die von Base erben (sofern sie dies nicht tun) getattribute natürlich nicht überschreiben ).
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")
Der Nachteil dieses Ansatzes ist getAttribute Haken alle Zugriff auf Attribute, so dass die Überprüfung von und potenzielle Umwickeln von Methoden auch für Attribute auf, der nicht Methoden sind + nicht die Funktion für den bestimmten Anruf in Frage gestellt werden , nutzen. Und die Verwendung von getattribute ist von Natur aus etwas kompliziert.
Die tatsächlichen Auswirkungen dieses Overheads waren meiner Erfahrung nach / für meine Zwecke vernachlässigbar, und auf meiner Maschine wird ein Dual-Core-Celeron ausgeführt. Die vorherige Umsetzung verwendete ich introspected Methoden auf Objekt init und banden die Runtime_Decorator Verfahren dann. Auf diese Weise wurde die Verwendung von getattribute überflüssig und der zuvor erwähnte Overhead reduziert. Es bricht jedoch auch Gurke (möglicherweise nicht Dill) und ist weniger dynamisch als dieser Ansatz.
Die einzigen Anwendungsfälle, auf die ich mit dieser Technik tatsächlich "in the wild" gestoßen bin, waren Timing- und Tracing-Dekorateure. Die Möglichkeiten, die sich daraus ergeben, sind jedoch äußerst vielfältig.
Wenn Sie eine bereits vorhandene Klasse haben, die nicht von einer anderen Basis geerbt werden kann (oder die Technik verwenden, die ihre eigene Klassendefinition oder die Basisklasse enthält), dann trifft das Ganze leider überhaupt nicht auf Ihr Problem zu.
Ich denke nicht, dass das Festlegen / Entfernen nicht aufrufbarer Attribute für eine Klasse zur Laufzeit unbedingt so herausfordernd ist. es sei denn, Sie möchten, dass Klassen, die von der geänderten Klasse erben, automatisch auch die Änderungen in sich selbst widerspiegeln ... Das wäre jedoch eine ganze Reihe weiterer Würmer.
quelle