인스턴스 변수를 자동으로 초기화 하시겠습니까?
다음과 같은 파이썬 클래스가 있습니다.
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
이것은 특히 당신의 수업이 정말로 큰 가치의 가방 일 때 적절합니다.
Zen of Python 인용 ,
명시적인 것이 암시적인 것보다 낫습니다.
Python 3.7+의 경우 원하는 작업을 수행하는 매우 비단뱀적이고 유지 관리 가능한 방법 인 Data Class를 사용할 수 있습니다 .
자동으로 초기화되는 인스턴스 변수 인 클래스에 대한 필드 를 정의 할 수 있습니다 .
다음과 같이 보일 것입니다.
@dataclass
class Process:
PID: int
PPID: int
cmd: str
...
__init__
방법은 이미 클래스에있을 것입니다.
여기에 있습니다 힌트를 입력이 필요합니다 내가 사용하는 이유입니다, int
그리고 str
예에서. 필드 유형을 모르는 경우 typing
모듈 에서 Any를 사용할 수 있습니다 .
데이터 클래스는 제안 된 솔루션에 비해 많은 장점이 있습니다.
- 그것은는 명시 적으로 모든 필드는 파이썬의 선을 존중하고 읽기 쉽고 유지 보수하게하는, 볼 수 있습니다. 그것을 사용하는
**kwargs
. - 메서드 를 가질 수 있습니다 . 다른 클래스와 마찬가지로. 메서드를 사용하려는 경우 메서드가 없다는 것은
namedtuple
. - 그것은 방법을
__init__
사용하여 자동으로 넘어갈 수 있습니다__post_init__
.
당신이 할 수있는 또 다른 일 :
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"
나는 같은 목적을 위해 무언가가 필요했지만 기존 답변 중 내가 테스트 한 모든 사례를 다루지 않았습니다. Nadia의 대답은 내가 찾던 것과 가장 가깝기 때문에 그녀의 코드를 기반으로 시작했습니다.
아래 데코레이터는 모든 유효한 인수 조합과 함께 작동합니다.
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 )
또한 클래스 인스턴스에 할당되지 않는 -private 변수 _
를 허용하기 위해 표준 -prefix 규칙을 구현합니다 __init__
.
### 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
모든 $
문자에 개행 문자를 사용하여 모든 잠재적 사용 사례를 자세히 설명하는 테스트를 확장 할 수 있습니다 .
locals ()에 이미 값이 포함되어 있으므로 변수를 초기화 할 필요가 없습니다!
클래스 더미 (객체) :
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.params [ 'b']
삼
물론 클래스 내에서 self.params를 사용할 수 있습니다.
getargspec
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")
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'
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'
Maybe this is a closed question, but I would like to propose my solution in order to know what you think about it. I have used a metaclass which applies a decorator to init method
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
The attrs library does something like this.
참고URL : https://stackoverflow.com/questions/1389180/automatically-initialize-instance-variables
'Programing' 카테고리의 다른 글
IntelliJ IDEA로 JUnit 설정 (0) | 2020.10.18 |
---|---|
Visual Studio bin 및 obj 폴더를 정리하는 방법 (0) | 2020.10.18 |
파일 다운로드없이 원격 파일 크기 (0) | 2020.10.18 |
'do… while'대 'while' (0) | 2020.10.18 |
Private / Protected 메서드는 단위 테스트를 받아야합니까? (0) | 2020.10.18 |