Eu tenho uma classe Python que se parece com isto:
class Process:
def __init__(self, PID, PPID, cmd, FDs, reachable, user):
Seguido por:
self.PID=PID
self.PPID=PPID
self.cmd=cmd
...
Existe alguma maneira de autoinicializar essas variáveis de instância, como a lista de inicialização do C ++? Isso pouparia muitos códigos redundantes.
python
class
initialization-list
Adam Matan
fonte
fonte
autoassign
receita de ativação e umaautoargs
implementação alternativa em: Qual é a melhor maneira de fazer atribuição automática de atributos em Python, e é uma boa ideia? - Stack OverflowRespostas:
Você pode usar um decorador:
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
Use-o para decorar o
__init__
método:class process: @initializer def __init__(self, PID, PPID, cmd, FDs, reachable, user): pass
Resultado:
>>> c = process(1, 2, 3, 4, 5, 6) >>> c.PID 1 >>> dir(c) ['FDs', 'PID', 'PPID', '__doc__', '__init__', '__module__', 'cmd', 'reachable', 'user'
fonte
Se estiver usando o Python 2.6 ou superior, você pode usar Collections.namedtuple :
>>> from collections import namedtuple >>> Process = namedtuple('Process', 'PID PPID cmd') >>> proc = Process(1, 2, 3) >>> proc.PID 1 >>> proc.PPID 2
Isso é apropriado especialmente quando sua classe é, na verdade, apenas um grande saco de valores.
fonte
Para Python 3.7+, você pode usar uma classe de dados , que é uma maneira muito pítônica e de fácil manutenção de fazer o que você quiser.
Ele permite que você defina campos para sua classe, que são suas variáveis de instância inicializadas automaticamente.
Seria algo assim:
@dataclass class Process: PID: int PPID: int cmd: str ...
O
__init__
método já estará em sua aula.Observe que aqui a sugestão de tipo é necessária , é por isso que usei
int
estr
no exemplo. Se você não souber o tipo do seu campo, pode usar Qualquer dotyping
módulo .A Data Class tem muitas vantagens em comparação com as soluções propostas:
**kwargs
.__init__
usando o__post_init__
método.EDIT: Razões para evitar o uso de NamedTuples
Alguns sugerem o uso de
namedtuple
para este caso, mas as duplas nomeadas têm alguns comportamentos que diferem das classes Python, que não são realmente evidentes no início e devem ser bem conhecidos:1. NamedTuples são imutáveis
A imutabilidade pode ser útil, mas talvez não seja o que você deseja para suas instâncias. DataClasses também pode ser imutável de alguma forma se você usar o argumento
frozen=True
no@dataclass
decorador.2. NamedTuples
__eq__
se comporta como o de TupleEm Python,
SomeNamedTuple(a=1, b=2) == AnotherNamedTuple(c=1, d=2)
éTrue
! A__eq__
função de NamedTuple, usada em comparações, considera apenas os valores e as posições desses valores nas instâncias comparadas, não seus nomes de classe ou campos.fonte
Data Classes can be thought of as "mutable namedtuples with defaults".
- PEP557Citando o Zen do Python ,
fonte
Outra coisa que você pode fazer:
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`
Mas a única solução que eu recomendaria, além de soletrar, é "faça uma macro em seu editor" ;-p
fonte
Você poderia fazer isso facilmente com os argumentos de palavra-chave, por exemplo:
>>> class D: def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) >>> D(test='d').test 'd'
implementação semelhante para os argumentos posicionais seria:
>> class C: def __init__(self, *args): self.t, self.d = args >>> C('abc', 'def').t 'abc' >>> C('abc', 'def').d 'def'
que para mim não parece resolver seu problema.
fonte
self.__dict__.update( **kwargs )
A solução de Nádia é melhor e mais poderosa, mas acho que isso também é interessante:
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"
fonte
Eu precisava de algo para o mesmo propósito, mas nenhuma das respostas existentes cobriu todos os casos que testei. A resposta de Nadia foi a mais próxima do que eu estava procurando, então comecei com seu código como base.
O decorador abaixo funciona com todas as combinações válidas de argumentos:
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 )
Ele também implementa a
_
convenção -prefix padrão para permitir__init__
variáveis -private que não serão atribuídas a instâncias de classe.### 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"))
Nota:
Eu incluí testes, mas os reduzi na última linha ( 58 ) para abreviar. Você pode expandir os testes, que detalham todos os casos de uso em potencial,
find/replace
-ing todos os$
caracteres com uma nova linha.fonte
Pode não ser necessário inicializar variáveis, pois locals () já contém os valores!
classe Dummy (objeto):
def __init__(self, a, b, default='Fred'): self.params = {k:v for k,v in locals().items() if k != 'self'}
d = Fictício (2, 3)
d.params
{'a': 2, 'b': 3, 'padrão': 'Fred'}
d.params ['b']
3
Claro, dentro de uma classe pode-se usar self.params
fonte
d['b']
é escrita na língua franca do Python, emborad.params['b']
possa causar confusão para os leitores de código.Assim que
getargspec
estiver obsoleto desde o Python 3.5, aqui está a solução usandoinspect.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
Verifique se funciona:
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")
fonte
Para 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
Demo:
>>> 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)
Uma abordagem não decoradora para Python 2 e 3 usando frames:
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)
Demo:
>>> p = Point('foo', 'bar') >>> p.x 'foo' >>> p.y 'bar'
fonte
nu11ptr fez um pequeno módulo, PyInstanceVars , que inclui essa funcionalidade como um decorador de função. No README do módulo está afirmado que o " [...] desempenho agora é apenas 30-40% pior do que a inicialização explícita sob CPython ".
Exemplo de uso, retirado diretamente da documentação do módulo :
>>> 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'
fonte
Talvez seja uma questão fechada, mas gostaria de propor minha solução para saber o que você pensa a respeito. Eu usei uma metaclasse que aplica um decorador ao método 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
fonte
A biblioteca attrs faz algo assim.
fonte
no final da função init :
for var in list(locals().keys()): setattr(self,var,locals()[var])
Para mais informações
setattr()
, consulte aquifonte
Há uma função auxiliar para fazer isso no 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.
fonte