복붙노트

[PYTHON] 파이썬 : 런타임시 메소드와 속성 변경하기

PYTHON

파이썬 : 런타임시 메소드와 속성 변경하기

파이썬에서 속성과 메소드를 추가하고 제거 할 수있는 클래스를 만들고 싶습니다. 어떻게 그것을 성취 할 수 있습니까?

왜 그런지 물어 보지 마세요.

해결법

  1. ==============================

    1.

    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'
    
  2. ==============================

    2.이 예제는 클래스에 메소드를 추가하는 것과 인스턴스에 메소드를 추가하는 것의 차이점을 보여줍니다.

    이 예제는 클래스에 메소드를 추가하는 것과 인스턴스에 메소드를 추가하는 것의 차이점을 보여줍니다.

    >>> 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'
    
  3. ==============================

    3.types.MethodType을 다음에서 사용할 때 흥미로운 대안 :

    types.MethodType을 다음에서 사용할 때 흥미로운 대안 :

    >>> f = types.MethodType(talk, puppy, Dog)
    >>> puppy.talk = f # add method to specific instance
    

    함수가 기술자라는 사실을 악용하는 것입니다 :

    >>> puppy.talk = talk.__get__(puppy, Dog)
    
  4. ==============================

    4.속성 및 메소드를 모든 클래스에 추가 및 제거 할 수 있으며 클래스의 모든 인스턴스에서 속성 및 메소드를 사용할 수 있습니다.

    속성 및 메소드를 모든 클래스에 추가 및 제거 할 수 있으며 클래스의 모든 인스턴스에서 속성 및 메소드를 사용할 수 있습니다.

    >>> 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'
    
  5. ==============================

    5.원래 클래스 이름에 액세스하거나 __class__를 통해 클래스에 직접 할당 할 수 있습니다.

    원래 클래스 이름에 액세스하거나 __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)
    
  6. ==============================

    6.또 다른 대안은, 당신이 수업을 대체하는 것이 필요한 경우 class 속성을 수정하는 것입니다 :

    또 다른 대안은, 당신이 수업을 대체하는 것이 필요한 경우 class 속성을 수정하는 것입니다 :

    >>> 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
    
  7. ==============================

    7.간단히:

    간단히:

    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
    
  8. ==============================

    8.클래스 자체를 반드시 수정해야합니까? 아니면 단순히 런타임에 특정 지점에서 object.method ()를 대체하는 것이 목표입니까?

    클래스 자체를 반드시 수정해야합니까? 아니면 단순히 런타임에 특정 지점에서 object.method ()를 대체하는 것이 목표입니까?

    내 기본 상속 개체에 getattribute 및 런타임 데코레이터를 사용하여 내 프레임 워크에서 원숭이 패치 특정 메서드를 호출하도록 클래스를 실제로 수정하는 문제를 회피했기 때문에 묻습니다.

    getattribute에서 Base 객체에 의해 검색된 메소드는 데코레이터 / 원숭이 패치 적용을위한 메소드 호출 키워드 인수를 파싱하는 Runtime_Decorator에 래핑됩니다.

    따라서 object.method (monkey_patch = "mypatch"), object.method (decorator = "mydecorator") 및 object.method (decorators = my_decorator_list) 구문을 사용할 수 있습니다.

    이 방법은 어떤 개별 메소드 호출 (마법 메서드를 사용하지 않음)에서 작동하며, 클래스 / 인스턴스 속성을 실제로 수정하지 않고 임의의 심지어 외래 메소드를 패치 할 수 있으며 Base에서 상속받은 하위 클래스에서 투명하게 작동합니다 (제공 한 경우 물론 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를 사용하는 것은 본질적으로 다소 복잡합니다.

    내 경험 / 목적에 대한이 오버 헤드의 실제 영향은 무시할만큼 낮았고, 내 컴퓨터는 듀얼 코어 셀러론을 실행합니다. 이전 구현에서는 객체 초기화시 introspected 메소드를 사용하고 Runtime_Decorator를 메소드에 바인딩했습니다. 그런 식으로 물건을 활용할 필요성을 없애고 이전에 언급 한 오버 헤드를 줄였습니다 ... 그러나 피클을 깨기도하고 (아마도 딜링이 아닐 수도 있습니다)이 방법은 덜 동적입니다.

    내가 실제로이 기술로 "야생에서"만난 유일한 유스 케이스는 타이밍 및 데코레이터와 관련이있었습니다. 그러나 그것이 열리는 가능성은 매우 광범위합니다.

    다른 기본 클래스에서 상속 할 수없는 기존 클래스가 있거나 자체 클래스 정의 또는 기본 클래스에있는 기술을 활용하면 모든 것이 단순히 불행히도 해당 문제에 적용되지 않습니다.

    나는 런타임에 클래스에서 호출 불가능한 속성을 설정 / 제거하는 것이 반드시 어렵다고 생각하지 않습니까? 수정 된 클래스에서 상속받은 클래스가 자동으로 변경 사항을 자동으로 반영하지 못하게하려는 경우가 아니라면 ... '소리를 들으면 전체적으로'할 수 있습니다.

  9. from https://stackoverflow.com/questions/962962/python-changing-methods-and-attributes-at-runtime by cc-by-sa and MIT license