[python] 인스턴스 변수를 자동으로 초기화 하시겠습니까?

다음과 같은 파이썬 클래스가 있습니다.

class Process:
    def __init__(self, PID, PPID, cmd, FDs, reachable, user):

뒤에 :

        self.PID=PID
        self.PPID=PPID
        self.cmd=cmd
        ...

C ++의 초기화 목록과 같이 이러한 인스턴스 변수를 자동 초기화하는 방법이 있습니까? 많은 중복 코드를 절약 할 수 있습니다.



답변

데코레이터를 사용할 수 있습니다.

from functools import wraps
import inspect

def initializer(func):
    """
    Automatically assigns the parameters.

    >>> class process:
    ...     @initializer
    ...     def __init__(self, cmd, reachable=False, user='root'):
    ...         pass
    >>> p = process('halt', True)
    >>> p.cmd, p.reachable, p.user
    ('halt', True, 'root')
    """
    names, varargs, keywords, defaults = inspect.getargspec(func)

    @wraps(func)
    def wrapper(self, *args, **kargs):
        for name, arg in list(zip(names[1:], args)) + list(kargs.items()):
            setattr(self, name, arg)

        for name, default in zip(reversed(names), reversed(defaults)):
            if not hasattr(self, name):
                setattr(self, name, default)

        func(self, *args, **kargs)

    return wrapper

그것을 사용하여 __init__방법 을 장식하십시오 .

class process:
    @initializer
    def __init__(self, PID, PPID, cmd, FDs, reachable, user):
        pass

산출:

>>> c = process(1, 2, 3, 4, 5, 6)
>>> c.PID
1
>>> dir(c)
['FDs', 'PID', 'PPID', '__doc__', '__init__', '__module__', 'cmd', 'reachable', 'user'


답변

Python 2.6 이상을 사용하는 경우 collections.namedtuple 을 사용할 수 있습니다 .

>>> from collections import namedtuple
>>> Process = namedtuple('Process', 'PID PPID cmd')
>>> proc = Process(1, 2, 3)
>>> proc.PID
1
>>> proc.PPID
2

이것은 특히 당신의 수업이 정말로 큰 가치의 가방 일 때 적절합니다.


답변

Python 3.7+의 경우 원하는 작업을 수행하는 매우 비단뱀적이고 유지 관리 가능한 방법 인 Data Class를 사용할 수 있습니다 .

자동으로 초기화되는 인스턴스 변수 인 클래스에 대한 필드 를 정의 할 수 있습니다 .

다음과 같이 보일 것입니다.

@dataclass
class Process:
    PID: int
    PPID: int
    cmd: str
    ...

__init__방법은 이미 클래스에있을 것입니다.

여기에 있습니다 힌트를 입력이 필요합니다 내가 사용하는 이유입니다, int그리고 str예에서. 필드 유형을 모르는 경우 typing모듈 에서 Any를 사용할 수 있습니다 .

데이터 클래스는 제안 된 솔루션에 비해 많은 장점이 있습니다.

  • 그것은는 명시 적으로 모든 필드는 파이썬의 선을 존중하고 읽기 쉽고 유지 보수하게하는, 볼 수 있습니다. 그것을 사용하는 **kwargs.
  • 메서드 를 가질 수 있습니다 . 다른 클래스와 마찬가지로.
  • 그것은 방법을 __init__사용하여 자동으로 넘어갈 수 있습니다 __post_init__.

편집 : NamedTuples 사용을 피하는 이유

일부는 namedtuple이 경우에 의 사용을 제안 하지만 namedtuple에는 Python 클래스와 다른 동작이 있습니다. 처음에는 분명하지 않으며 잘 알려져 있어야합니다.

1. NamedTuples는 불변입니다.

불변성은 유용 할 수 있지만 인스턴스에 원하는 것이 아닐 수도 있습니다. 당신이 인수를 사용하는 경우 DataClasses도 어떻게 든 불변 수 있습니다 frozen=True@dataclass장식을.

2. NamedTuples __eq__는 Tuple처럼 동작합니다.

파이썬에서, SomeNamedTuple(a=1, b=2) == AnotherNamedTuple(c=1, d=2)입니다 True! __eq__비교에 사용되는 NamedTuple 의 함수는 해당 클래스 또는 필드의 이름이 아닌 비교 된 인스턴스에서 해당 값의 값과 위치 만 고려합니다.


답변

Zen of Python 인용 ,

명시적인 것이 암시적인 것보다 낫습니다.


답변

당신이 할 수있는 또 다른 일 :

class X(object):
    def __init__(self, a,b,c,d):
        vars = locals() # dict of local names
        self.__dict__.update(vars) # __dict__ holds and object's attributes
        del self.__dict__["self"] # don't need `self`

하지만 내가 추천하는 유일한 해결책은 철자를 쓰는 것 외에 “편집기에서 매크로를 만드는 것”입니다. ;-p


답변

다음과 같이 키워드 인자로 쉽게 할 수 있습니다.

>>> class D:
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

>>> D(test='d').test
'd'

위치 인수에 대한 유사한 구현은 다음과 같습니다.

>> class C:
    def __init__(self, *args):
        self.t, self.d = args


>>> C('abc', 'def').t
'abc'
>>> C('abc', 'def').d
'def'

나에게 당신의 문제를 해결하지 못하는 것 같습니다.


답변

Nadia의 솔루션이 더 좋고 강력하지만 이것도 흥미 롭다고 생각합니다.

def constructor(*arg_names):
  def __init__(self, *args):
    for name, val in zip(arg_names, args):
      self.__setattr__(name, val)
  return __init__


class MyClass(object):
  __init__ = constructor("var1", "var2", "var3")


>>> c = MyClass("fish", "cheese", "beans")
>>> c.var2
"cheese"