स्वचालित रूप से उदाहरण चर शुरू?


89

मेरे पास एक अजगर वर्ग है जो इस तरह दिखता है:

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

के बाद:

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

क्या इन उदाहरण चर को स्वचालित करने का कोई तरीका है, जैसे C ++ की आरंभीकरण सूची? यह बहुत सारे बेमानी कोड को छोड़ देगा।


जवाबों:


104

आप एक डेकोरेटर का उपयोग कर सकते हैं:

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'

5
यह काम करता है और सवाल का जवाब देता है इसलिए मैंने मतदान किया। लेकिन मैंने फर्डिंड बेयर को जवाब दिया: "स्पष्ट रूप से निहित से बेहतर है"
लुकास गेब्रियल

14
+1 महान जवाब के लिए जिसने मेरी समस्या हल कर दी। लेकिन क्या यह भाषा की मुख्य कार्यक्षमता नहीं होनी चाहिए? क्या आपको लगता है कि यह पीईपी लिखने के लायक है?
एडम मेटन

3
यह एक बहुत अच्छा जवाब है - यह सीधे मेरे टूलबॉक्स में चला गया है।
माइकल वान डेर वेस्टहुइज़न

3
@NadiaAlramli मुझे लगता है कि कोड में एक छोटा सा बग है। यहाँ देखें gist.github.com/pmav99/137dbf4681be9a58de74 (original.py)
pmav99

2
वर्तमान उदाहरण में बग है, और यदि हस्ताक्षर डिफ़ॉल्ट तर्कों को शामिल नहीं करता है तो यह काम नहीं करेगा। आपको यह सुनिश्चित करने के लिए एक चेक शामिल करना होगा कि नाम और चूक कोई नहीं हैं। Ex: यदि नाम और चूक:

36

आप अजगर 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

यह विशेष रूप से उचित है जब आपकी कक्षा वास्तव में मूल्यों का एक बड़ा बैग है।


2
"यह विशेष रूप से उचित है जब आपकी कक्षा वास्तव में मूल्यों का एक बड़ा बैग है।" ऐसे परिदृश्य में, आप यह भी कर सकते हैं: https://docs.python.org/3.3/tutorial/classes.html#odds-and-ends
Big Sharpie

34

Python 3.7+ के लिए आप एक Data Class का उपयोग कर सकते हैं , जो कि आप जो चाहते हैं वह करने के लिए एक बहुत ही आकर्षक और अनुरक्षित तरीका है।

यह आपको अपनी कक्षा के लिए उन क्षेत्रों को परिभाषित करने की अनुमति देता है , जो आपके स्वचालित रूप से प्रारंभिक उदाहरण चर हैं।

यह कुछ इस तरह दिखेगा:

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

__init__पद्धति पहले से ही अपनी कक्षा में हो जाएगा।

ध्यान दें कि यहां टाइप हिंटिंग की आवश्यकता है , यही कारण है कि मैंने उपयोग किया है intऔर strउदाहरण में। यदि आप अपने क्षेत्र के प्रकार को नहीं जानते हैं, तो आप मॉड्यूल से कोई भीtyping उपयोग कर सकते हैं ।

प्रस्तावित समाधानों की तुलना में डेटा क्लास के कई फायदे हैं:

  • यह स्पष्ट है : सभी क्षेत्र दिखाई देते हैं, जो पायथन के ज़ेन का सम्मान करता है और इसे पठनीय और बनाए रखने योग्य बनाता है। के उपयोग से इसकी तुलना करें **kwargs
  • इसके तरीके हो सकते हैं । किसी भी अन्य वर्ग की तरह।
  • यह आपको विधि __init__का उपयोग करके स्वचालित से आगे जाने की अनुमति देता है __post_init__

EDIT: NamedTuples के उपयोग से बचने के कारण

कुछ namedtupleइस मामले के लिए उपयोग करने का सुझाव देते हैं , लेकिन नामितों के पास कुछ व्यवहार हैं जो पायथन कक्षाओं से भिन्न हैं, जो वास्तव में पहली बार में स्पष्ट नहीं हैं और अच्छी तरह से जाना जाना चाहिए:

1. NamedTuples अपरिवर्तनीय हैं

अपरिवर्तनशीलता उपयोगी हो सकती है, लेकिन शायद यह वह नहीं है जो आप अपने उदाहरणों के लिए चाहते हैं। यदि आप डेकोरेटर frozen=Trueपर तर्क का उपयोग करते हैं तो DataClasses भी किसी तरह अपरिवर्तनीय हो सकता है @dataclass

2. NamedTuples __eq__Tuple की तरह व्यवहार करता है

पायथन में, SomeNamedTuple(a=1, b=2) == AnotherNamedTuple(c=1, d=2)है True! __eq__NamedTuple का कार्य, तुलना में उपयोग किया जाता है, केवल उन उदाहरणों और उन मूल्यों के पदों की तुलना उदाहरणों पर करता है, न कि उनके वर्ग या क्षेत्रों के नामों पर विचार करता है।


इसका उपयोग केवल तभी किया जाना चाहिए जब वर्ग का उद्देश्य डेटा संग्रहीत करना है।
जेसी रोकोमांडे

या भंडारण डेटा के आसपास विकसित करने के लिए।
बजे जेसी रोकोमांडे

3
क्या आप बताएंगे कि डेटा को संग्रहित करने वाली कक्षाओं के लिए डेटालैस का ही उपयोग क्यों किया जाना चाहिए, बजाय इसके कि अन्य व्यवहार भी हो? मैं इसके उपयुक्त उपयोग के मामलों को बेहतर ढंग से समझने के लिए इसके लिए एक नया SO पोस्ट बना सकता हूं। धन्यवाद।
वाहिद पजीरंदेह

Data Classes can be thought of as "mutable namedtuples with defaults". - PEP557
एनाफुली

26

पायथन के ज़ेन का हवाला देते हुए ,

निहितार्थ की तुलना में स्पष्ट है।


10
क्या प्रारंभिक सूची की घोषणा पर्याप्त रूप से स्पष्ट नहीं होगी?
एडम मेटन

मेरा अनुमान। लेकिन मुझे भाषा में ऐसा कुछ जोड़ने का कारण नहीं दिखता। मैं स्पष्ट रूप से पर्दे के पीछे किसी तरह के डेकोरेटर-मैजिक के कई असाइनमेंट स्टेटमेंट को पसंद करता हूं।
फर्डिनेंड बेयर सेप

29
@Ferdinand, मैं मानता हूं कि भाषा में ऐसा कुछ होना मूर्खतापूर्ण होगा जो stdlib में पूरी तरह से अच्छी तरह से हो सकता है, लेकिन, यह stdlib में होना चाहिए, क्योंकि "सुंदर बदसूरत से बेहतर होता है", यह बहुत अच्छा लगता है, और कई दोहराव वाले कार्य बदसूरत होते हैं। (जैसा कि पुनरावृत्ति का कोई रूप है)।
एलेक्स मार्टेली

ठीक है, काउंटर करने के लिए: डीडब्लूआईएम> डीडब्लूआईएस
टेरेंस

मैं सहमत हूँ कि डेकोरेटर असाइनमेंट्स की एक सूची से अधिक सुंदर है, लेकिन PyCharm इसे समझ नहीं कर इसे बदसूरत बना देता है :-(
user110954

23

एक और चीज जो आप कर सकते हैं:

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`

लेकिन केवल एक ही समाधान मैं सुझाऊंगा, इसके अलावा इसे बाहर वर्तनी के अलावा, "अपने संपादक में एक मैक्रो बनाओ"; -पी


1
'स्व' को हटाने पर अच्छी पकड़।
माइकल

15

आप इसे कीवर्ड तर्क के साथ आसानी से कर सकते हैं, जैसे:

>>> 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'

जो मुझे आपकी समस्या को हल करने के लिए प्रतीत नहीं होता है।


3
एक और भिन्नता जो मुझे पसंद है वह हैself.__dict__.update( **kwargs )
S.Lott

साथ ही स्थानीय लोगों का उपयोग करें () और सामान्य तर्क रखें।
mk12

7

नादिया का समाधान बेहतर और अधिक शक्तिशाली है, लेकिन मुझे लगता है कि यह भी दिलचस्प है:

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"

5

मुझे इसी उद्देश्य के लिए कुछ चाहिए था, लेकिन मेरे द्वारा परीक्षण किए गए सभी मामलों में से कोई भी मौजूदा उत्तर नहीं था। नादिया का जवाब, जो मैं देख रहा था, उसके सबसे करीब था, इसलिए मैंने आधार के रूप में उनके कोड के साथ शुरुआत की।

नीचे दिए गए डेकोरेटर तर्कों के सभी मान्य संयोजनों के साथ काम करते हैं:

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        )

यह मानक _-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सभी $पात्रों को एक नई रेखा के साथ।


3

वैरिएबल को इनिशियलाइज़ करने की आवश्यकता नहीं हो सकती है, क्योंकि लोकल () में पहले से ही वैल्यूज़ होती हैं!

कक्षा डमी (वस्तु):

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

डी = डमी (2, 3)

d.params

{'a': 2, 'b': 3, 'डिफ़ॉल्ट': 'Fred'}

d.params [ 'बी']

3

बेशक, एक वर्ग के भीतर एक self.params का उपयोग कर सकता है


यह एक अच्छा और मूल दृष्टिकोण है, लेकिन d['b']पायथन के लिंगुआ फ्रेंका में लिखा गया है , जबकि d.params['b']कोड पाठकों के लिए भ्रम पैदा करेगा।
एडम मटन

3

जैसे ही getargspecPython 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")

2

पायथन 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)

फ्रेम का उपयोग करके पायथन 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'

1

nu11ptr ने एक छोटा मॉड्यूल बनाया है, PyInstanceVars , जिसमें फंक्शन डेकोरेटर के रूप में यह कार्यक्षमता शामिल है। मॉड्यूल के README में कहा गया है कि " [...] प्रदर्शन अब केवल CP-40 के तहत स्पष्ट आरंभीकरण की तुलना में 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'

0

हो सकता है कि यह एक बंद प्रश्न है, लेकिन मैं आपके समाधान का प्रस्ताव करना चाहता हूं ताकि आप यह जान सकें कि आप इसके बारे में क्या सोचते हैं। मैंने एक मेटाक्लस का उपयोग किया है जो एक डेकोरेटर इनिट विधि को लागू करता है

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


0

init फ़ंक्शन के अंत में :

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

अधिक जानकारी के लिए setattr()कृपया उल्लेख यहाँ


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.