Saya memiliki kelas python yang terlihat seperti ini:
class Process:
def __init__(self, PID, PPID, cmd, FDs, reachable, user):
diikuti oleh:
self.PID=PID
self.PPID=PPID
self.cmd=cmd
...
Apakah ada cara untuk menginisialisasi variabel instance ini secara otomatis, seperti daftar inisialisasi C ++? Ini akan menghemat banyak kode yang berlebihan.
python
class
initialization-list
Adam Matan
sumber
sumber
autoassign
resep aktivestate, danautoargs
implementasi alternatif di: Apa cara terbaik untuk melakukan penetapan atribut otomatis dengan Python, dan apakah itu ide yang bagus? - Stack OverflowJawaban:
Anda dapat menggunakan dekorator:
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
Gunakan untuk menghias
__init__
metode:class process: @initializer def __init__(self, PID, PPID, cmd, FDs, reachable, user): pass
Keluaran:
>>> c = process(1, 2, 3, 4, 5, 6) >>> c.PID 1 >>> dir(c) ['FDs', 'PID', 'PPID', '__doc__', '__init__', '__module__', 'cmd', 'reachable', 'user'
sumber
Jika Anda menggunakan Python 2.6 atau lebih tinggi, Anda dapat menggunakan collections.namedtuple :
>>> from collections import namedtuple >>> Process = namedtuple('Process', 'PID PPID cmd') >>> proc = Process(1, 2, 3) >>> proc.PID 1 >>> proc.PPID 2
Ini tepat terutama ketika kelas Anda benar-benar hanya sekantong besar nilai.
sumber
Untuk Python 3.7+ Anda dapat menggunakan Kelas Data , yang merupakan cara yang sangat pythonic dan dapat dipelihara untuk melakukan apa yang Anda inginkan.
Ini memungkinkan Anda untuk menentukan bidang untuk kelas Anda, yang merupakan variabel instance yang diinisialisasi secara otomatis.
Ini akan terlihat seperti itu:
@dataclass class Process: PID: int PPID: int cmd: str ...
Itu
__init__
Metode sudah akan berada di kelas Anda.Perhatikan bahwa di sini diperlukan petunjuk jenis , itulah sebabnya saya menggunakan
int
danstr
dalam contoh. Jika Anda tidak mengetahui jenis bidang Anda, Anda dapat menggunakan Any from thetyping
module .Kelas Data memiliki banyak keunggulan dibandingkan dengan solusi yang diusulkan:
**kwargs
.__init__
menggunakan__post_init__
metode ini.EDIT: Alasan untuk menghindari penggunaan NamedTuples
Beberapa menyarankan penggunaan
namedtuple
untuk kasus ini, tetapi tuple bernama memiliki beberapa perilaku yang berbeda dari kelas Python, yang pada awalnya tidak terlalu jelas dan harus diketahui dengan baik:1. NamedTuple tidak dapat diubah
Kekekalan dapat berguna, tetapi mungkin itu bukan yang Anda inginkan untuk instance Anda. DataClasses juga bisa berubah jika Anda menggunakan argumen
frozen=True
pada@dataclass
dekorator.2. NamedTuple
__eq__
berperilaku seperti Tuple'sDengan Python,
SomeNamedTuple(a=1, b=2) == AnotherNamedTuple(c=1, d=2)
adalahTrue
! The__eq__
fungsi NamedTuple, yang digunakan dalam perbandingan, hanya mempertimbangkan nilai-nilai dan posisi nilai-nilai pada kasus dibandingkan, bukan nama kelas atau ladang mereka.sumber
Data Classes can be thought of as "mutable namedtuples with defaults".
- PEP557Mengutip Zen of Python ,
sumber
Hal lain yang dapat Anda lakukan:
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`
Tapi satu-satunya solusi yang saya rekomendasikan, selain hanya mengejanya, adalah "buat makro di editor Anda" ;-p
sumber
Anda dapat melakukannya dengan mudah dengan argumen kata kunci, misalnya seperti ini:
>>> class D: def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) >>> D(test='d').test 'd'
implementasi serupa untuk argumen posisi adalah:
>> class C: def __init__(self, *args): self.t, self.d = args >>> C('abc', 'def').t 'abc' >>> C('abc', 'def').d 'def'
yang bagi saya sepertinya tidak menyelesaikan masalah Anda.
sumber
self.__dict__.update( **kwargs )
Solusi Nadia lebih baik dan lebih kuat, tapi menurut saya ini juga menarik:
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"
sumber
Saya membutuhkan sesuatu untuk tujuan yang sama, tetapi tidak ada jawaban yang mencakup semua kasus yang saya uji. Jawaban Nadia paling dekat dengan apa yang saya cari, jadi saya mulai dengan kodenya sebagai basis.
Dekorator di bawah ini berfungsi dengan semua kombinasi argumen yang valid:
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 )
Ini juga mengimplementasikan
_
konvensi -prefix standar untuk memungkinkan__init__
variabel -private yang tidak akan ditetapkan ke instance kelas.### 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"))
catatan:
Saya menyertakan tes, tetapi menciutkannya ke baris terakhir ( 58 ) agar lebih singkat. Anda dapat memperluas pengujian, yang merinci semua kasus penggunaan potensial, dengan
find/replace
-ing semua$
karakter dengan baris baru.sumber
Mungkin tidak perlu menginisialisasi variabel, karena local () sudah berisi nilainya!
class Dummy (objek):
def __init__(self, a, b, default='Fred'): self.params = {k:v for k,v in locals().items() if k != 'self'}
d = Dummy (2, 3)
d. parameter
{'a': 2, 'b': 3, 'default': 'Fred'}
d.params ['b']
3
Tentu saja, dalam kelas seseorang dapat menggunakan self.params
sumber
d['b']
ditulis dalam lingua franca Python sementarad.params['b']
akan menyebabkan kebingungan bagi pembaca kode.Segera setelah
getargspec
tidak digunakan lagi sejak Python 3.5, inilah solusi yang menggunakaninspect.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
Periksa apakah berfungsi:
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")
sumber
Untuk 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)
Pendekatan non-dekorator untuk Python 2 dan 3 menggunakan bingkai:
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'
sumber
nu11ptr telah membuat modul kecil, PyInstanceVars , yang menyertakan fungsionalitas ini sebagai dekorator fungsi. Dalam modul README dinyatakan bahwa kinerja " [...] sekarang hanya 30-40% lebih buruk daripada inisialisasi eksplisit di bawah CPython ".
Contoh penggunaan, diambil langsung dari dokumentasi modul :
>>> 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'
sumber
Mungkin ini pertanyaan tertutup, tetapi saya ingin mengusulkan solusi saya untuk mengetahui pendapat Anda tentang hal itu. Saya telah menggunakan metaclass yang menerapkan dekorator ke metode 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
sumber
The attrs perpustakaan melakukan sesuatu seperti ini.
sumber
di akhir fungsi init :
for var in list(locals().keys()): setattr(self,var,locals()[var])
Untuk informasi lebih lanjut,
setattr()
silakan lihat di sinisumber
Ada fungsi pembantu untuk melakukan ini di lib fastcore 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.
sumber