programing

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

oldcodes 2023. 7. 28. 22:35
반응형

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

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

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 3.7+의 경우 데이터 클래스를 사용할 수 있습니다. 데이터 클래스는 사용자가 원하는 작업을 수행하는 매우 파이썬적이고 유지 관리 가능한 방법입니다.

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

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

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

__init__메소드가 이미 클래스에 있을 것입니다.

여기서 유형 힌트가 필요하므로 사용한 이유는 다음과 같습니다.int그리고.str예문에서필드 유형을 모를 경우 모듈에서 Any를 사용할 수 있습니다.

데이터 클래스는 제안된 솔루션과 비교하여 많은 이점을 제공합니다.

  • 명시적입니다. 모든 필드가 표시되며, 이는 Python의 Zen을 존중하고 읽기 쉽고 유지 관리 가능하게 합니다.의 용도와 비교해 보십시오.**kwargs.
  • 방법이 있을 수 있습니다.다른 수업과 마찬가지로.
  • 인 것을 넘어서게 해줍니다.__init__방법을 사용합니다.

편집: 명명된 사용을 피해야 하는 이유튜플

일부는 의 사용을 제안합니다.namedtuple은 Python 클래스와 몇 가지 , 잘 있지 잘 합니다.

명명됨튜플은 불변입니다.

불변성은 유용할 수 있지만, 여러분이 원하는 것이 아닐지도 모릅니다.하면 DataClasss가 .frozen=True에서.@dataclass장식가

이 붙은 튜플스__eq__플처행니다처럼 합니다.

파썬에서이,SomeNamedTuple(a=1, b=2) == AnotherNamedTuple(c=1, d=2)이라True__eq__되는 Named 수명tTuple의 합니다.비교에 사용되는 Tuple은 클래스나 필드의 이름이 아닌 비교된 인스턴스의 값과 위치만 고려합니다.

Python 2.6 이상을 사용하는 경우 collections.nametuple:

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

이것은 특히 여러분의 수업이 정말로 가치가 있는 큰 가방일 때 적절합니다.

파이썬의 선을 인용하자면,

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

또 다른 방법은 다음과 같은 것이 있습니다.

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"

저는 같은 목적을 위해 무언가가 필요했지만, 제가 테스트한 모든 사례를 다루는 기존의 답변은 없었습니다.나디아의 대답은 내가 찾던 것과 가장 가까웠기 때문에, 나는 그녀의 코드를 기반으로 시작했습니다.

아래 데코레이터는 모든 유효한 인수 조합과 함께 작동합니다.

Positional                                          __init__(self, a, b                )
Keyword                                             __init__(self, a=None, b=None      )
Positional + Keyword                                __init__(self, a, b, c=None, d=None)
Variable Positional                                 __init__(self, *a                  )
Variable Positional + Keyword                       __init__(self, *a, b=None          )
Variable Positional + Variable Keyword              __init__(self, *a, **kwargs        )
Positional + Variable Positional + Keyword          __init__(self, a, *b, c=None       )
Positional + Variable Positional + Variable Keyword __init__(self, a, *b, **kwargs     )
Keyword Only                                        __init__(self, *, a=None           )
Positional + Keyword Only                           __init__(self, a, *, b=None        )

또한 표준을 구현합니다._- ▁-▁▁for를 하는 -▁convprefix 규약.__init__클래스 인스턴스에 할당되지 않는 -private 변수입니다.


###  StdLib  ###
from functools import wraps
import inspect


###########################################################################################################################
#//////|   Decorator   |//////////////////////////////////////////////////////////////////////////////////////////////////#
###########################################################################################################################

def auto_assign_arguments(function):

  @wraps(function)
  def wrapped(self, *args, **kwargs):
    _assign_args(self, list(args), kwargs, function)
    function(self, *args, **kwargs)

  return wrapped


###########################################################################################################################
#//////|   Utils   |//////////////////////////////////////////////////////////////////////////////////////////////////////#
###########################################################################################################################

def _assign_args(instance, args, kwargs, function):

  def set_attribute(instance, parameter, default_arg):
    if not(parameter.startswith("_")):
      setattr(instance, parameter, default_arg)

  def assign_keyword_defaults(parameters, defaults):
    for parameter, default_arg in zip(reversed(parameters), reversed(defaults)):
      set_attribute(instance, parameter, default_arg)

  def assign_positional_args(parameters, args):
    for parameter, arg in zip(parameters, args.copy()):
      set_attribute(instance, parameter, arg)
      args.remove(arg)

  def assign_keyword_args(kwargs):
    for parameter, arg in kwargs.items():
      set_attribute(instance, parameter, arg)
  def assign_keyword_only_defaults(defaults):
    return assign_keyword_args(defaults)

  def assign_variable_args(parameter, args):
    set_attribute(instance, parameter, args)

  POSITIONAL_PARAMS, VARIABLE_PARAM, _, KEYWORD_DEFAULTS, _, KEYWORD_ONLY_DEFAULTS, _ = inspect.getfullargspec(function)
  POSITIONAL_PARAMS = POSITIONAL_PARAMS[1:] # remove 'self'

  if(KEYWORD_DEFAULTS     ): assign_keyword_defaults     (parameters=POSITIONAL_PARAMS,  defaults=KEYWORD_DEFAULTS)
  if(KEYWORD_ONLY_DEFAULTS): assign_keyword_only_defaults(defaults=KEYWORD_ONLY_DEFAULTS                          )
  if(args                 ): assign_positional_args      (parameters=POSITIONAL_PARAMS,  args=args                )
  if(kwargs               ): assign_keyword_args         (kwargs=kwargs                                           )
  if(VARIABLE_PARAM       ): assign_variable_args        (parameter=VARIABLE_PARAM,      args=args                )


###########################################################################################################################$#//////|   Tests   |//////////////////////////////////////////////////////////////////////////////////////////////////////#$###########################################################################################################################$$if __name__ == "__main__":$$#######|   Positional   |##################################################################################################$$  class T:$    @auto_assign_arguments$    def __init__(self, a, b):$      pass$$  t = T(1, 2)$  assert (t.a == 1) and (t.b == 2)$$#######|   Keyword   |#####################################################################################################$$  class T:$    @auto_assign_arguments$    def __init__(self, a="KW_DEFAULT_1", b="KW_DEFAULT_2"):$      pass$$  t = T(a="kw_arg_1", b="kw_arg_2")$  assert (t.a == "kw_arg_1") and (t.b == "kw_arg_2")$$#######|   Positional + Keyword   |########################################################################################$$  class T:$    @auto_assign_arguments$    def __init__(self, a, b, c="KW_DEFAULT_1", d="KW_DEFAULT_2"):$      pass$$  t = T(1, 2)$  assert (t.a == 1) and (t.b == 2) and (t.c == "KW_DEFAULT_1") and (t.d == "KW_DEFAULT_2")$$  t = T(1, 2, c="kw_arg_1")$  assert (t.a == 1) and (t.b == 2) and (t.c == "kw_arg_1") and (t.d == "KW_DEFAULT_2")$$  t = T(1, 2, d="kw_arg_2")$  assert (t.a == 1) and (t.b == 2) and (t.c == "KW_DEFAULT_1") and (t.d == "kw_arg_2")$$#######|   Variable Positional   |#########################################################################################$$  class T:$    @auto_assign_arguments$    def __init__(self, *a):$      pass$$  t = T(1, 2, 3)$  assert (t.a == [1, 2, 3])$$#######|   Variable Positional + Keyword   |###############################################################################$$  class T:$    @auto_assign_arguments$    def __init__(self, *a, b="KW_DEFAULT_1"):$      pass$$  t = T(1, 2, 3)$  assert (t.a == [1, 2, 3]) and (t.b == "KW_DEFAULT_1")$$  t = T(1, 2, 3, b="kw_arg_1")$  assert (t.a == [1, 2, 3]) and (t.b == "kw_arg_1")$$#######|   Variable Positional + Variable Keyword   |######################################################################$$  class T:$    @auto_assign_arguments$    def __init__(self, *a, **kwargs):$      pass$$  t = T(1, 2, 3, b="kw_arg_1", c="kw_arg_2")$  assert (t.a == [1, 2, 3]) and (t.b == "kw_arg_1") and (t.c == "kw_arg_2")$$#######|   Positional + Variable Positional + Keyword   |##################################################################$$  class T:$    @auto_assign_arguments$    def __init__(self, a, *b, c="KW_DEFAULT_1"):$      pass$$  t = T(1, 2, 3, c="kw_arg_1")$  assert (t.a == 1) and (t.b == [2, 3]) and (t.c == "kw_arg_1")$$#######|   Positional + Variable Positional + Variable Keyword   |#########################################################$$  class T:$    @auto_assign_arguments$    def __init__(self, a, *b, **kwargs):$      pass$$  t = T(1, 2, 3, c="kw_arg_1", d="kw_arg_2")$  assert (t.a == 1) and (t.b == [2, 3]) and (t.c == "kw_arg_1") and (t.d == "kw_arg_2")$$#######|   Keyword Only   |################################################################################################$$  class T:$    @auto_assign_arguments$    def __init__(self, *, a="KW_DEFAULT_1"):$      pass$$  t = T(a="kw_arg_1")$  assert (t.a == "kw_arg_1")$$#######|   Positional + Keyword Only   |###################################################################################$$  class T:$    @auto_assign_arguments$    def __init__(self, a, *, b="KW_DEFAULT_1"):$      pass$$  t = T(1)$  assert (t.a == 1) and (t.b == "KW_DEFAULT_1")$$  t = T(1, b="kw_arg_1")$  assert (t.a == 1) and (t.b == "kw_arg_1")$$#######|   Private __init__ Variables (underscored)   |####################################################################$$  class T:$    @auto_assign_arguments$    def __init__(self, a, b, _c):$      pass$$  t = T(1, 2, 3)$  assert hasattr(t, "a") and hasattr(t, "b") and not(hasattr(t, "_c"))

참고:

저는 테스트를 포함했지만, 간결함을 위해 마지막 줄(58)로 정리했습니다.모든 잠재적인 사용 사례를 자세히 설명하는 테스트를 다음과 같이 확장할 수 있습니다.find/replace-모든 것을$줄이 바뀐 문자

local()에 이미 값이 포함되어 있기 때문에 변수를 초기화할 필요가 없을 수 있습니다.

클래스 더미(개체):

def __init__(self, a, b, default='Fred'):
    self.params = {k:v for k,v in locals().items() if k != 'self'}

d = 더미(2, 3)

d.파람스

{'a': 2, 'b': 3, 'default': 'Fred'}

d.파람['b']

3

물론 클래스 내에서 self.params를 사용할 수 있습니다.

하자마자getargspec 3되지 않습니다. Python 3.5를 사용하는 . 다음 솔루션은inspect.signature:

from inspect import signature, Parameter
import functools


def auto_assign(func):
    # Signature:
    sig = signature(func)
    for name, param in sig.parameters.items():
        if param.kind in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD):
            raise RuntimeError('Unable to auto assign if *args or **kwargs in signature.')
    # Wrapper:
    @functools.wraps(func)
    def wrapper(self, *args, **kwargs):
        for i, (name, param) in enumerate(sig.parameters.items()):
            # Skip 'self' param:
            if i == 0: continue
            # Search value in args, kwargs or defaults:
            if i - 1 < len(args):
                val = args[i - 1]
            elif name in kwargs:
                val = kwargs[name]
            else:
                val = param.default
            setattr(self, name, val)
        func(self, *args, **kwargs)
    return wrapper

작동하는지 확인:

class Foo(object):
    @auto_assign
    def __init__(self, a, b, c=None, d=None, e=3):
        pass

f = Foo(1, 2, d="a")
assert f.a == 1
assert f.b == 2
assert f.c is None
assert f.d == "a"
assert f.e == 3

print("Ok")

Python 3.3+의 경우:

from functools import wraps
from inspect import Parameter, signature


def instance_variables(f):
    sig = signature(f)
    @wraps(f)
    def wrapper(self, *args, **kwargs):
        values = sig.bind(self, *args, **kwargs)
        for k, p in sig.parameters.items():
            if k != 'self':
                if k in values.arguments:
                    val = values.arguments[k]
                    if p.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY):
                        setattr(self, k, val)
                    elif p.kind == Parameter.VAR_KEYWORD:
                        for k, v in values.arguments[k].items():
                            setattr(self, k, v) 
                else:
                    setattr(self, k, p.default) 
    return wrapper

class Point(object):
    @instance_variables 
    def __init__(self, x, y, z=1, *, m='meh', **kwargs):
        pass

데모:

>>> p = Point('foo', 'bar', r=100, u=200)
>>> p.x, p.y, p.z, p.m, p.r, p.u
('foo', 'bar', 1, 'meh', 100, 200)

프레임을 사용하는 Python 2와 3 모두를 위한 비데코레이터 접근 방식:

import inspect


def populate_self(self):
    frame = inspect.getouterframes(inspect.currentframe())[1][0]
    for k, v in frame.f_locals.items():
        if k != 'self':
            setattr(self, k, v)


class Point(object):
    def __init__(self, x, y):
        populate_self(self)

데모:

>>> p = Point('foo', 'bar')
>>> p.x
'foo'
>>> p.y
'bar'

init 함수의 끝에서:

for var in list(locals().keys()):
    setattr(self,var,locals()[var])

에 대한 자세한 setattr()여기를 참조해 주십시오

nu11ptr은 기능 장식가로서 이 기능을 포함하는 작은 모듈인 PyInstanceVars를 만들었습니다.모듈의 README에는 "[...] 성능이 CPython에서 명시적 초기화보다 30-40%만 더 나쁘다"고 명시되어 있습니다.

모듈의 설명서에서 직접 가져온 사용 예:

>>> from instancevars import *
>>> class TestMe(object):
...     @instancevars(omit=['arg2_'])
...     def __init__(self, _arg1, arg2_, arg3='test'):
...             self.arg2 = arg2_ + 1
...
>>> testme = TestMe(1, 2)
>>> testme._arg1
1
>>> testme.arg2_
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'TestMe' object has no attribute 'arg2_'
>>> testme.arg2
3
>>> testme.arg3
'test'

fastcore lib https://fastcore.fast.ai/utils.html#store_attr 에는 이를 위한 도우미 기능이 있습니다.

from fastcore.utils import store_attr

class Process:
    def __init__(self, PID, PPID, cmd, FDs, reachable, user):
        store_attr() # this will do the same as self.PID = PID etc.

아마도 이것은 비공개 질문일 수도 있지만, 저는 당신이 그것에 대해 어떻게 생각하는지 알기 위해 저의 해결책을 제안하고 싶습니다.init 메서드에 데코레이터를 적용하는 메타클래스를 사용했습니다.

import inspect

class AutoInit(type):
    def __new__(meta, classname, supers, classdict):
        classdict['__init__'] = wrapper(classdict['__init__'])
        return type.__new__(meta, classname, supers, classdict)

def wrapper(old_init):
    def autoinit(*args):
        formals = inspect.getfullargspec(old_init).args
        for name, value in zip(formals[1:], args[1:]):
            setattr(args[0], name, value)
    return autoinit

아트스 도서관은 이런 일을 합니다.

입력 집합이 고정되어 있는 경우 다음과 같은 간단한 솔루션을 사용할 수 있습니다.

from inspect import getargvalues, stack

def arguments():
    args = getargvalues(stack()[1][0])[-1]
    del args['self']
    if 'kwargs' in args:
        args.update(args['kwargs'])
        del args['kwargs']
    return args

class Process():
    def __init__(self, PID, PPID, cmd, FDs, reachable, user):
        # Auto update all arguments into object dictionary
        self.__dict__.update(arguments())

*kwargs를 지정하지 않은 경우 이 작업을 수행해야 합니다.

object = Process(1,2,3,'foo','random'...)
# all the right instances will be created
# object.PID =1
# object.PPID = 2

언급URL : https://stackoverflow.com/questions/1389180/automatically-initialize-instance-variables

반응형