IT Share you

파이썬에서 클래스에 대한 클래스 메서드를 동적으로 만드는 방법

shareyou 2020. 12. 12. 12:39
반응형

파이썬에서 클래스에 대한 클래스 메서드를 동적으로 만드는 방법


이 질문에 이미 답변이 있습니다.

작은 파이썬 프로그램을 다음과 같이 정의하면

class a():
    def _func(self):
        return "asdf"

    # Not sure what to resplace __init__ with so that a.func will return asdf
    def __init__(self, *args, **kwargs):
         setattr(self, 'func', classmethod(self._func))

if __name__ == "__main__":
    a.func

역 추적 오류가 발생합니다.

Traceback (most recent call last):
  File "setattr_static.py", line 9, in <module>
    a.func
AttributeError: class a has no attribute 'func'

내가 알아 내려고하는 것은 객체를 인스턴스화하지 않고 클래스 메서드를 클래스에 동적으로 설정하는 방법입니다.


편집하다:

이 문제에 대한 답은

class a():
    pass

def func(cls, some_other_argument):
    return some_other_argument

setattr(a, 'func', classmethod(func))

if __name__ == "__main__":
    print(a.func)
    print(a.func("asdf"))

다음 출력을 반환합니다.

<bound method type.func of <class '__main__.a'>>
asdf

클래스 객체에 대한 간단한 할당이나 클래스 객체의 setattr에 의해 클래스에 클래스 메서드를 동적으로 추가 할 수 있습니다. 여기에서는 혼란을 줄이기 위해 클래스가 대문자로 시작하는 파이썬 규칙을 사용하고 있습니다.

# define a class object (your class may be more complicated than this...)
class A(object):
    pass

# a class method takes the class object as its first variable
def func(cls):
    print 'I am a class method'

# you can just add it to the class if you already know the name you want to use
A.func = classmethod(func)

# or you can auto-generate the name and set it this way
the_name = 'other_func' 
setattr(A, the_name, classmethod(func))

여기에 몇 가지 문제가 있습니다.

  • __init__인스턴스를 만들 때만 실행됩니다 (예 : obj = a(). 이것은 당신이 할 때 a.func, setattr()전화가 발생하지 않았 음을 의미합니다.
  • 당신은 그냥 사용하는 그래서 그 대신, 직접 그 클래스의 메서드 내에서 클래스의 속성에 액세스 할 수없는 _func내부의 __init__사용해야합니다 당신 self._func또는self.__class__._func
  • self인스턴스에 a속성을 설정하면 클래스가 아닌 해당 인스턴스에 대해서만 사용할 수 있습니다. 그래서 심지어 호출 한 후 setattr(self, 'func', self._func), a.funcAttributeError를 제기합니다
  • staticmethod당신이하는 방식을 사용 하면 아무것도 staticmethod하지 않고 결과 함수를 반환하며 인수를 수정하지 않습니다. 따라서 대신 다음과 같은 것을 원할 것입니다 setattr(self, 'func', staticmethod(self._func))(그러나 위의 주석을 고려하면 여전히 작동하지 않습니다)

이제 문제는 실제로 무엇을하려고 하는가입니다. 인스턴스를 초기화 할 때 클래스에 속성을 실제로 추가하려면 다음과 같이 할 수 있습니다.

class a():
    def _func(self):
        return "asdf"

    def __init__(self, *args, **kwargs):
        setattr(self.__class__, 'func', staticmethod(self._func))

if __name__ == '__main__':
    obj = a()
    a.func
    a.func()

그러나 이것은 여전히 ​​이상합니다. 이제 a.func문제없이 액세스 하고 호출 할 수 있지만의 self인수 a.func는 항상 가장 최근에 생성 된 a. 인스턴스 메서드 _func()를 정적 메서드 나 클래스의 클래스 메서드로 바꾸는 건전한 방법을 정말로 생각할 수 없습니다 .

클래스에 함수를 동적으로 추가하려고하므로 다음과 같은 것이 실제로 수행하려는 작업에 더 가깝습니까?

class a():
    pass

def _func():
    return "asdf"

a.func = staticmethod(_func)  # or setattr(a, 'func', staticmethod(_func))

if __name__ == '__main__':
    a.func
    a.func()

이런 식으로 할 수 있습니다

class a():
    def _func(self):
        return "asdf"

setattr(a, 'func', staticmethod(a._func))

if __name__ == "__main__":
    a.func()

1. 기본 아이디어 : 메서드를 보유하기 위해 추가 클래스 사용

작업을 수행하는 의미있는 방법을 찾았습니다.

먼저 다음과 같은 BaseClass를 정의합니다.

class MethodPatcher:
    @classmethod
    def patch(cls, target):
        for k in cls.__dict__:
            obj = getattr(cls, k)
            if not k.startswith('_') and callable(obj):
                setattr(target, k, obj)

이제 원래 클래스가 생겼습니다.

class MyClass(object):
    def a(self):
        print('a')

그런 다음 새 Patcher클래스 에 추가하려는 새 메서드를 정의합니다 .

( _이 경우 메서드 이름을로 시작하지 마십시오 )

class MyPatcher(MethodPatcher):
    def b(self):
        print('b')

그런 다음 전화 :

MyPatcher.patch(MyClass)

따라서 새 메서드 b(self)가 원본에 추가 되었음을 알 수 있습니다 MyClass.

obj = MyClass()
obj.a()  # which prints an 'a'
obj.b()  # which prints a 'b'

2. 구문을 덜 장황하게 만들고 클래스 데코레이터를 사용합니다.

이제 MethodPatcher데칼 이 있으면 두 가지 작업을 수행해야합니다.

  • 추가 할 추가 메서드를 포함 하는 자식 클래스 ChildClass정의 ModelPatcher합니다.
  • 요구 ChildClass.patch(TargetClass)

그래서 우리는 곧 데코레이터를 사용하여 두 번째 단계를 단순화 할 수 있음을 발견했습니다.

데코레이터를 정의합니다.

def patch_methods(model_class):
    def do_patch(cls):
        cls.patch(model_class)
    return do_patch

그리고 다음과 같이 사용할 수 있습니다.

@patch_methods(MyClass)
class MyClassPatcher(MethodPatcher):

    def extra_method_a(self):
        print('a', self)

    @classmethod
    def extra_class_method_b(cls):
        print('c', cls)

    # !!ATTENTION!! the effect on declaring staticmethod here may not work as expected:
    # calling this method on an instance will take the self into the first argument.
    # @staticmethod
    # def extra_static_method_c():
    #    print('c')

3. 함께 포장

그래서 우리는 지금의 정의를 넣을 수 있습니다 MethodPatcher그리고 patch_method하나의 모듈로를 :

# method_patcher.py

class MethodPatcher:
    @classmethod
    def patch(cls, target):
        for k in cls.__dict__:
            obj = getattr(cls, k)
            if not k.startswith('_') and callable(obj):
                setattr(target, k, obj)

def patch_methods(model_class):
    def do_patch(cls):
        cls.patch(model_class)
    return do_patch

따라서 자유롭게 사용할 수 있습니다.

from method_patcher import ModelPatcher, patch_model

4. 최종 솔루션 : 더 간단한 선언

Soon I found that the MethodPatcher class is not nessesary, while the @patch_method decorator can do the work, so FINALLY we only need a patch_method:

def patch_methods(model_class):
    def do_patch(cls):
        for k in cls.__dict__:
            obj = getattr(cls, k)
            if not k.startswith('_') and callable(obj):
                setattr(model_class, k, obj)
    return do_patch

And the usage becomes:

@patch_methods(MyClass)
class MyClassPatcher:

    def extra_method_a(self):
        print('a', self)

    @classmethod
    def extra_class_method_b(cls):
        print('c', cls)

    # !!ATTENTION!! the effect on declaring staticmethod here may not work as expected:
    # calling this method on an instance will take the self into the first argument.
    # @staticmethod
    # def extra_static_method_c():
    #    print('c')

You need to setattr(self, 'func', staticmethod(self._func))

You need to initialize class variable=a() to call __init__ There is no init in static class


I'm using Python 2.7.5, and I wasn't able to get the above solutions working for me. This is what I ended up with:

# define a class object (your class may be more complicated than this...)
class A(object):
    pass

def func(self):
    print 'I am class {}'.format(self.name)

A.func = func

# using classmethod() here failed with:
#       AttributeError: type object '...' has no attribute 'name'

참고URL : https://stackoverflow.com/questions/17929543/how-can-i-dynamically-create-class-methods-for-a-class-in-python

반응형