आप अजगर में चर तर्कों (kwargs) से वर्ग विशेषताओं को कैसे सेट कर सकते हैं


119

मान लीजिए कि मेरे पास एक कंस्ट्रक्टर (या अन्य फ़ंक्शन) के साथ एक वर्ग है जो एक चर संख्या के तर्क लेता है और फिर उन्हें सशर्त रूप से वर्ग विशेषताओं के रूप में सेट करता है।

मैं उन्हें मैन्युअल रूप से सेट कर सकता था, लेकिन ऐसा लगता है कि चर पैरामीटर अजगर में पर्याप्त सामान्य हैं कि ऐसा करने के लिए एक सामान्य मुहावरा होना चाहिए। लेकिन मुझे यकीन नहीं है कि यह कैसे गतिशील है।

मेरे पास eval का उपयोग करके एक उदाहरण है, लेकिन यह शायद ही सुरक्षित है। मैं ऐसा करने का उचित तरीका जानना चाहता हूं - शायद मेमने के साथ?

class Foo:
    def setAllManually(self, a=None, b=None, c=None):
        if a!=None: 
            self.a = a
        if b!=None:
            self.b = b
        if c!=None:
            self.c = c
    def setAllWithEval(self, **kwargs):
        for key in **kwargs:
            if kwargs[param] != None
                eval("self." + key + "=" + kwargs[param])

ऐसा लगता है कि ये प्रश्न समान हैं: stackoverflow.com/questions/3884612/… stackoverflow.com/questions/356718/… stackoverflow.com/questions/1446555/… तो ऐसा लगता है कि मैं जो चाहता हूँ, वह शायद यह है- self________ [कुंजी] = kwargs [कुंजी]
— १

वास्तव में आपके प्रश्न के लिए प्रासंगिक नहीं है, लेकिन आप पारंपरिक पायथन स्टाइल पर कुछ संकेतों के लिए पीईपी 8 की जांच करना चाहते हैं ।
— थॉमस ओरोज़्को

इस कहा जाता है के लिए एक शानदार पुस्तकालय है। बस pip install attrs, के साथ अपनी कक्षा को सजाने के लिए @attr.s, और a = attr.ib(); b = attr.ib()आदि के रूप में सेट करें ।
— एडम बार्न्स

क्या मुझसे कोई चूक हो रही है? आपको अभी भी self.x = kwargs.get'x 'करने की आवश्यकता है] आप अपने आप को कॉलर से टाइप करने के लिए खोलते हैं। आपको ग्राहक को अतिरिक्त वर्ण उदाहरण = वर्ग (** {}) के साथ बनाना होगा यदि आप थ्रू हुप्स नहीं करते हैं self.x = kwargs.get'x '] सांसारिकता, क्या यह आपको बाद में काटने नहीं जा रहा है? यानी स्वयं के बजाय। आप स्वयं के साथ समाप्त कर लेंगे .__ तानाशाही __ ['x'] सही रेखा के नीचे? या getattr () स्वयं की तुलना में अधिक टाइपिंग।
— JGFMK

जवाबों:


148

आप __dict__कीवर्ड तर्क के साथ विशेषता (जो एक शब्दकोश के रूप में वर्ग विशेषताओं का प्रतिनिधित्व करते हैं) को अपडेट कर सकते हैं :

class Bar(object):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

तब आप कर सकते हो:

>>> bar = Bar(a=1, b=2)
>>> bar.a
1

और कुछ इस तरह से:

allowed_keys = {'a', 'b', 'c'}
self.__dict__.update((k, v) for k, v in kwargs.items() if k in allowed_keys)

आप पहले से कुंजियों को फ़िल्टर कर सकते हैं ( यदि आप अभी भी Python 2.x का उपयोग कर रहे हैं तो iteritemsइसके बजाय उपयोग करें items)।


2
इससे भी बेहतर अगर आप self.__dict__.update(locals())स्थितिगत तर्कों को कॉपी करने के लिए उपयोग करते हैं।
— जियानकार्लो स्पोर्टेली

2
मुझे लगता है कि आजकल आपको इसकी आवश्यकता होगी .. kwargs [0] .items () के बजाय kwargs.iteritems () - (मैं लेखन के समय पायथॉन 3.6.5 का उपयोग कर रहा हूं)
— JGFMK

@JGFMK kwargs[0]सिर्फ के बजाय क्यों kwargs? क्या kwargsपूर्णांक कुंजी भी हो सकती है ? मुझे पूरा यकीन है कि उन्हें स्ट्रिंग्स बनना होगा।
— वेजेंड्रिया

146

आप setattr()विधि का उपयोग कर सकते हैं :

class Foo:
  def setAllWithKwArgs(self, **kwargs):
    for key, value in kwargs.items():
      setattr(self, key, value)

getattr()विशेषताओं को पुनः प्राप्त करने के लिए एक अनुरूप विधि है।


@ लार्क्स धन्यवाद लेकिन किसी भी विचार हम केवल एक शब्दकोश कुंजी कैसे खोल सकता है? stackoverflow.com/questions/41792761/…
— जिनसो

क्या आपको उपयोग करने की आवश्यकता है .getattr()? या आप के साथ विशेषताओं का उपयोग कर सकते हैं Foo.key?
— स्टेवोइसाक

@StevenVascellaro आप बस का उपयोग कर सकते हैं Foo.attrname। मुझे लगता है कि मैं इस तथ्य को इंगित कर रहा था कि getattrविधि मौजूद है। यह तब भी उपयोगी है जब आप नामांकित विशेषता उपलब्ध नहीं होने पर डिफ़ॉल्ट मान प्रदान करना चाहते हैं।
— लार्क्स

3
स्वीकृत उत्तर के साथ क्या अंतर है ? । क्या उनके पेशेवरों और विपक्ष?
— एडुआर्डो पिग्नाटेली

15

यहां अधिकांश उत्तर केवल एक डिफ़ॉल्ट मान के लिए सभी अनुमत विशेषताओं को प्रारंभ करने के लिए एक अच्छा तरीका कवर नहीं करते हैं। तो, @fqxp और @mmj द्वारा दिए गए उत्तरों को जोड़ने के लिए :

class Myclass:

    def __init__(self, **kwargs):
        # all those keys will be initialized as class attributes
        allowed_keys = set(['attr1','attr2','attr3'])
        # initialize all allowed keys to false
        self.__dict__.update((key, False) for key in allowed_keys)
        # and update the given keys by their given values
        self.__dict__.update((key, value) for key, value in kwargs.items() if key in allowed_keys)

मुझे लगता है कि यह inizialization के कारण सबसे पूर्ण उत्तर है False। अच्छी बात!
— Kyrol

9

मैं fxxp के उत्तर की भिन्नता का प्रस्ताव करता हूं , जो अनुमत विशेषताओं के अतिरिक्त , आपको विशेषताओं के लिए डिफ़ॉल्ट मान सेट करने देता है :

class Foo():
    def __init__(self, **kwargs):
        # define default attributes
        default_attr = dict(a=0, b=None, c=True)
        # define (additional) allowed attributes with no default value
        more_allowed_attr = ['d','e','f']
        allowed_attr = list(default_attr.keys()) + more_allowed_attr
        default_attr.update(kwargs)
        self.__dict__.update((k,v) for k,v in default_attr.items() if k in allowed_attr)

यह पायथन 3.x कोड है, Python 2.x के लिए आपको इसके स्थान पर कम से कम एक समायोजन की आवश्यकता iteritems()है items()।


1
यह सबसे लचीला उत्तर है, इस सूत्र में अन्य दृष्टिकोणों का सारांश है। यह विशेषताओं को सेट करता है, डिफ़ॉल्ट मानों की अनुमति देता है और केवल अनुमत विशेषता नामों को जोड़ता है। यहां दिखाए गए अनुसार अजगर 3.x के साथ ठीक काम करता है।
— स्कवारस्पिरल

7

फिर भी mmj और fxxp द्वारा उत्कृष्ट उत्तरों के आधार पर एक और संस्करण । अगर हम चाहते हैं तो क्या होगा

  1. अनुमत विशेषताओं की सूची को हार्डकोड करने से बचें
  2. सीधे और स्पष्ट रूप से निर्माता में प्रत्येक विशेषताओं के लिए डिफ़ॉल्ट मान सेट करें
  3. या तो पूर्वनिर्धारित विशेषताओं के लिए kwargs प्रतिबंधित करें
    • चुपचाप अमान्य तर्कों को खारिज कर दिया या , वैकल्पिक रूप से,
    • कोई त्रुटि उठाना।

"सीधे" से मेरा मतलब है कि एक बाहरी default_attributesशब्दकोष से बचना ।

class Bar(object):
    def __init__(self, **kwargs):

        # Predefine attributes with default values
        self.a = 0
        self.b = 0
        self.A = True
        self.B = True

        # get a list of all predefined values directly from __dict__
        allowed_keys = list(self.__dict__.keys())

        # Update __dict__ but only for keys that have been predefined 
        # (silently ignore others)
        self.__dict__.update((key, value) for key, value in kwargs.items() 
                             if key in allowed_keys)

        # To NOT silently ignore rejected keys
        rejected_keys = set(kwargs.keys()) - set(allowed_keys)
        if rejected_keys:
            raise ValueError("Invalid arguments in constructor:{}".format(rejected_keys))

बड़ी सफलता नहीं, लेकिन शायद किसी के लिए उपयोगी ...

EDIT: यदि हमारी कक्षा @propertyडेकोरेटर का उपयोग गेटर्स और सेवर्स के साथ "संरक्षित" विशेषताओं को एनकैप्सुलेट करने के लिए करती है, और अगर हम अपने कंस्ट्रक्टर के साथ इन गुणों को सेट करने में सक्षम होना चाहते हैं, तो हम निम्नानुसार allowed_keysमानों के साथ सूची का विस्तार करना चाहते dir(self)हैं:

allowed_keys = [i for i in dir(self) if "__" not in i and any([j.endswith(i) for j in self.__dict__.keys()])]

उपरोक्त कोड शामिल नहीं है

  • किसी भी छिपे हुए चर dir()("__" की उपस्थिति के आधार पर बहिष्करण), और
  • किसी भी विधि से dir()जिसका नाम विशेषता नाम (संरक्षित या अन्यथा) के अंत में नहीं मिला है __dict__.keys(), जिससे संभवत: @property सजाया तरीकों को बनाए रखा जा सकता है।

यह संपादन संभवतः केवल पायथन 3 और उसके बाद के लिए मान्य है।


2
class SymbolDict(object):
  def __init__(self, **kwargs):
    for key in kwargs:
      setattr(self, key, kwargs[key])

x = SymbolDict(foo=1, bar='3')
assert x.foo == 1

मैंने कक्षा को बुलाया SymbolDictक्योंकि यह अनिवार्य रूप से एक शब्दकोश है जो तार के बजाय प्रतीकों का उपयोग करके संचालित होता है। दूसरे शब्दों में, आप इसके x.fooबजाय करते हैं x['foo']लेकिन कवर के तहत यह वास्तव में एक ही बात चल रही है।


2

निम्नलिखित समाधान vars(self).update(kwargs)या self.__dict__.update(**kwargs)क्योंकि उपयोगकर्ता कोई त्रुटि संदेश के साथ किसी भी शब्दकोश में प्रवेश कर सकते, मजबूत नहीं कर रहे हैं। अगर मुझे यह जांचने की आवश्यकता है कि उपयोगकर्ता निम्नलिखित हस्ताक्षर ('a1', 'a2', 'a3', 'a4', 'a5') सम्मिलित करता है तो समाधान काम नहीं करता है। इसके अलावा, उपयोगकर्ता को "स्थितिगत मापदंडों" या "का-मूल्य जोड़े मापदंडों" को पारित करके वस्तु का उपयोग करने में सक्षम होना चाहिए।

तो मैं एक मेटासेल का उपयोग करके निम्नलिखित समाधान सुझाता हूं।

from inspect import Parameter, Signature

class StructMeta(type):
    def __new__(cls, name, bases, dict):
        clsobj = super().__new__(cls, name, bases, dict)
        sig = cls.make_signature(clsobj._fields)
        setattr(clsobj, '__signature__', sig)
        return clsobj

def make_signature(names):
    return Signature(
        Parameter(v, Parameter.POSITIONAL_OR_KEYWORD) for v in names
    )

class Structure(metaclass = StructMeta):
    _fields = []
    def __init__(self, *args, **kwargs):
        bond = self.__signature__.bind(*args, **kwargs)
        for name, val in bond.arguments.items():
            setattr(self, name, val)

if __name__ == 'main':

   class A(Structure):
      _fields = ['a1', 'a2']

   if __name__ == '__main__':
      a = A(a1 = 1, a2 = 2)
      print(vars(a))

      a = A(**{a1: 1, a2: 2})
      print(vars(a))

1

उनका एक बेहतर समाधान हो सकता है, लेकिन मेरे लिए जो मन में आता है वह है:

class Test:
    def __init__(self, *args, **kwargs):
        self.args=dict(**kwargs)

    def getkwargs(self):
        print(self.args)

t=Test(a=1, b=2, c="cats")
t.getkwargs()


python Test.py 
{'a': 1, 'c': 'cats', 'b': 2}

मैं जो देख रहा हूं वह सत्यापन के आधार पर सशर्त रूप से सेट की गई विशेषता है। मुझे एहसास हुआ कि kwargs का उपयोग करने में समस्या यह है कि यह मान्य नहीं है (या दस्तावेज़) जो कि स्वीकार्य हैं
— fijiaaron

हाँ, मुझे एहसास है कि @ उत्तर बेहतर है। SO पर हर रोज कुछ नया सीखें!
— टॉम

1

यह लार्क्स के माध्यम से सबसे आसान है

class Foo:
    def setAllWithKwArgs(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)

मेरा उदाहरण:

class Foo:
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)

door = Foo(size='180x70', color='red chestnut', material='oak')
print(door.size) #180x70

smb समझा सकता है कि kwargs.items () क्या है?
— ओलेग_कोर्निलोव

kwargsखोजशब्द तर्कों का एक शब्दकोश है, और items()एक विधि है जो शब्दकोश की (key, value)जोड़ी की सूची की एक प्रति लौटाती है ।
— harryscholes 12

-1

मुझे लगता है कि यह सबसे उदाहरणों में बेहतर हो सकता है नामांकित आर्ग्स (बेहतर स्व दस्तावेज कोड के लिए) का उपयोग करें ताकि यह कुछ इस तरह दिखे:

class Foo:
    def setAll(a=None, b=None, c=None):
        for key, value in (a, b, c):
            if (value != None):
                settattr(self, key, value)

इस यात्रा काम नहीं करता है:for key, value in (a, b, c)
— rerx
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.