8 ответов:
Я хочу создать класс в Python, который я могу добавлять и удалять атрибуты и методы.
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'
в этом примере показаны различия между добавлением метода в класс и экземпляр.
>>> 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'
возможно интересная альтернатива использованию
types.MethodTypein:>>> f = types.MethodType(talk, puppy, Dog) >>> puppy.talk = f # add method to specific instanceбудет использовать тот факт, что функции дескрипторов:
>>> puppy.talk = talk.__get__(puppy, Dog)
Я хочу создать класс в Python, который я могу добавлять и удалять атрибуты и методы. Как я могу это сделать?
вы можете добавлять и удалять атрибуты и методы в любой класс, и они будут доступны для всех экземпляров класса:
>>> 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'
вы можете просто назначить непосредственно к классу (либо путем доступа к исходному имени класса или через
__class__):class a : pass ob=a() ob.__class__.blah=lambda self,k: (3, self,k) ob.blah(5) ob2=a() ob2.blah(7)печати
(3, <__main__.a instance at 0x7f18e3c345f0>, 5) (3, <__main__.a instance at 0x7f18e3c344d0>, 7)
другой вариант, если вам нужно заменить класс оптом является изменение класс:
>>> 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
просто:
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что приводит к:
['__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
обязательно ли нужно изменять сам класс? Или цель просто заменить какой-то объект.метод () делает в определенный момент во время выполнения?
Я спрашиваю, потому что я уклоняюсь от проблемы фактического изменения класса для конкретных вызовов метода monkey patch в моей структуре с getattribute и декоратор времени выполнения на моем базовом объекте наследования.
методы, полученные базовым объектом в getattribute обернуты в a Runtime_Decorator, который анализирует метод, вызывает аргументы ключевого слова для применения исправлений декораторов/обезьян.
Это позволяет использовать объект синтаксиса.метод (monkey_patch= "mypatch"), объект.способ(декоратор="mydecorator"), и даже объект.метод (декораторы=my_decorator_list).
это работает для любого отдельного вызова метода (я оставляю магические методы), делает это без фактического изменения каких-либо атрибутов класса/экземпляра, может использовать произвольные, даже иностранные методы для патч, и будет работать прозрачно на подкассах, которые наследуют от базы (при условии, что они не переопределяют 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")недостатком этого подхода является getattribute крючки все доступ к атрибутам, поэтому проверка и потенциальная упаковка методов происходит даже для атрибутов, которые не являются методами + не будет использовать функцию для конкретного вызова в вопросе. И используя getattribute на всех по своей сути это несколько сложно.
фактическое влияние этих накладных расходов в моем опыте/для моих целей было незначительным, и моя машина работает с двухъядерным Celeron. Предыдущей реализации я использовал методы анализировался на объект init и привязал Runtime_Decorator к методам тогда. Делая вещи таким образом устраняется необходимость использовать getattribute и уменьшил накладные расходы, упомянутые ранее... однако он также ломает рассол (возможно, нет укроп) и менее динамичен, чем этот подход.
единственные случаи использования, с которыми я действительно столкнулся "в дикой природе" с этой техникой, были с декораторами времени и трассировки. Однако возможности, которые он открывает, чрезвычайно широки.
Если у вас есть существующий класс, который невозможно наследовать от другой базы (или использовать технику, его собственное определение класса или в ее базовом классе), то все это просто не относится к вашей проблеме вообще к сожалению.
Я не думаю, что установка/удаление не востребованных атрибутов класса во время выполнения, обязательно так сложно? если вы не хотите, чтобы классы, которые наследуют от измененного класса, автоматически отражали изменения в себе... Хотя, судя по звуку, это была бы целая банка червей.
Comments