2012-08-15 13 views
22
class A(): pass 

a = A() 
b = A() 

a.b = b 
b.c = 1 

a.b  # this is b 
getattr(a, "b") # so is this 

a.b.c # this is 1 
getattr(a, "b.c") # this raises an AttributeError 

बाद वाले को मानना ​​मेरे लिए बहुत स्वाभाविक लग रहा था। मुझे यकीन है कि इसके लिए एक अच्छा कारण है। यह क्या है?`getattr` लगातार गुण पुनर्प्राप्ति का समर्थन क्यों नहीं करता है?

+4

मिलेगा अब यह करते हैं। 'Getattr (a, 'b.c') 'अब वापस क्या होना चाहिए? क्या होगा यदि पहले 'बी' पर कोई 'सी' नहीं था? आपको विशेषता नामों में '.' का उपयोग करने की अनुमति है, इसलिए आप इस तरह की वस्तुओं पर 'getattr' को पार करने में सक्षम होने की उम्मीद नहीं कर सकते हैं। –

उत्तर

30

आप गेटैटर फ़ंक्शन में कोई अवधि नहीं डाल सकते क्योंकि Getattr ऑब्जेक्ट का प्रत्यक्ष शब्दकोश लुकअप है।

यदि आप किसी पर 'डीआईआर' फ़ंक्शन का उपयोग करते हैं, तो आपको अपनी ऑब्जेक्ट के गुणों के अनुरूप शब्दकोश कुंजी दिखाई देगी। इस मामले में, "b.c" शब्दकोश कुंजी के सेट में नहीं है।

getattr के साथ ऐसा करने का एकमात्र तरीका घोंसला कॉल करने के लिए है:

getattr(getattr(a, "b"), "c") 

सौभाग्य से, मानक पुस्तकालय एक बेहतर समाधान है!

import operator 
operator.attrgetter("b.c")(a) 
+7

ओपी ने महसूस किया है कि ... मुझे लगता है कि वह क्यों पूछ रहा है। एक अनुचित प्रश्न नहीं दिया गया है कि ऑपरेटर.एटरजेटर ('ए.बी.') (ओबीजे) _will_ संकल्प बिंदीदार नोटेशन –

+0

इसे इंगित करने के लिए धन्यवाद। उम्मीद है कि मेरी प्रतिक्रिया का स्पष्ट रूप से व्याख्या करने के लिए मेरी प्रतिक्रिया स्पष्ट करें। –

+0

यह एक परेशानी की समस्या के लिए एक बहुत अच्छा और सरल समाधान है। नीचे दिए गए उत्तर संक्षेप में नहीं हैं। यह उत्तर कुछ ऐसा करने की कोशिश नहीं करता है जो पाइथन पहले से ही कर चुका है, जो इसे अच्छा बनाता है। – Bobort

6

क्योंकि getattr इस तरह से काम नहीं करता है। getattr किसी दिए गए नाम (द्वितीय तर्क) के साथ किसी दिए गए ऑब्जेक्ट (प्रथम तर्क) की विशेषता प्राप्त करता है। तो अपने कोड:

getattr(a, "b.c") # this raises an AttributeError 

का अर्थ है: पहुँच "ई.पू." द्वारा "एक" संदर्भित वस्तु की विशेषता। जाहिर है कि आपके ऑब्जेक्ट में "b.c" नामक विशेषता नहीं है।

प्राप्त करने के लिए "ग" विशेषता आपके दो getattr कॉल का उपयोग करना चाहिए:

getattr(getattr(a, "b"), "c") 

की यह बेहतर समझ के लिए खोलने करते हैं:

b = getattr(a, "b") 
c = getattr(b, "c") 
0

क्या getattr('a.b', {'a': None}, 'default-value'} लौट जाना चाहिए? क्या इसे AttributeError बढ़ाया जाना चाहिए या बस 'default-value' वापस करना चाहिए? यही कारण है कि getattr में पेश की जाने वाली जटिल कुंजी इसे उपयोग करने के लिए अस्पष्ट बनाती हैं।

तो, getattr(..) कार्य को ऑब्जेक्ट विशेषताओं के शब्दकोश के get विधि के रूप में देखना अधिक स्वाभाविक है।

4

मुझे लगता है कि आपका भ्रम इस तथ्य से उत्पन्न होता है कि सीधे डॉट नोटेशन (पूर्व a.b.c) getattr() के समान पैरामीटर तक पहुंचता है, लेकिन पार्सिंग तर्क अलग है। जबकि वे अनिवार्य रूप से किसी ऑब्जेक्ट की __dict__ विशेषता में कुंजी रखते हैं, getattr() डॉट-सुलभ विशेषताओं पर अधिक कठोर आवश्यकताओं के लिए बाध्य नहीं है। उदाहरण के लिए

setattr(foo, 'Big fat ugly string. But you can hash it.', 2) 

, वैध है के बाद से है कि स्ट्रिंग सिर्फ foo.__dict__ में एक हैश कुंजी हो जाता है, लेकिन

foo.Big fat ugly string. But you can hash it. = 2 

और

foo.'Big fat ugly string. But you can hash it.' = 2 

क्योंकि अब आप के लिए पूछ रहे हैं दुभाषिया वाक्य रचना त्रुटियाँ हैं इन चीजों को कच्चे कोड के रूप में पार्स करें, और यह काम नहीं करता है।

इस का दूसरा पहलू यह है कि जब foo.b.cfoo.__dict__['b'].__dict__['c'] के बराबर है, getattr(foo, 'b.c')foo.__dict__['b.c'] के बराबर है है। यही कारण है कि getattr काम नहीं कर रहा है जैसा आप उम्मीद कर रहे हैं।

+1

गोश, मुझे यह एक obfuscated कोड प्रतियोगिता में हो रहा है ... –

37

Python's built-in reduce function उस कार्यक्षमता को सक्षम बनाता है जिसे आप ढूंढ रहे हैं। यहां एक साधारण छोटा सहायक कार्य है जो काम पूरा करेगा:

class NoDefaultProvided(object): 
    pass 

def getattrd(obj, name, default=NoDefaultProvided): 
    """ 
    Same as getattr(), but allows dot notation lookup 
    Discussed in: 
    http://stackoverflow.com/questions/11975781 
    """ 

    try: 
     return reduce(getattr, name.split("."), obj) 
    except AttributeError, e: 
     if default != NoDefaultProvided: 
      return default 
     raise 

टेस्ट सबूत;

>>> getattrd(int, 'a') 
AttributeError: type object 'int' has no attribute 'a' 

>>> getattr(int, 'a') 
AttributeError: type object 'int' has no attribute 'a' 

>>> getattrd(int, 'a', None) 
None 

>>> getattr(int, 'a', None) 
None 

>>> getattrd(int, 'a', None) 
None 

>>> getattrd(int, '__class__.__name__') 
type 

>>> getattrd(int, '__class__') 
<type 'type'> 
+3

यह स्वीकार्य उत्तर imho होना चाहिए, ठाणे का जवाब बेकार के बगल में है। – sleepycal

+0

मैं सब एक अच्छे जवाब के लिए हूं, और यह काम पूरा हो जाता है। लेकिन यह जवाब ओपी को वह नहीं देता जो वह ढूंढ रहा था - * कारण क्यों * यह काम नहीं करता है। –

+0

स्वीकार्य उत्तर मूल समस्या को हल नहीं करता है। यह मानते हुए कि आपको केवल 'b.c' स्ट्रिंग दी गई है, यह एकमात्र उत्तर है जो समस्या को हल करता है। –

0

आप प्रत्येक डॉट ऑपरेटर के लिए

def multi_getattr(self,obj, attr, default = None): 
      attributes = attr.split(".") 
      for i in attributes: 
       try: 
        obj = getattr(obj, i) 
       except AttributeError: 
        if default: 
         return default 
        else: 
         raise 
      return obj 

डॉट ऑपरेटरों बंटवारे और एक getattr() प्रदर्शन से समारोह के भीतर एक समारोह बुला बिना एकाधिक getattr कॉल कर सकते हैं, तो मान लीजिए कि आप आप कर सकते हैं ABCD कॉल करना चाहते हैं इसे a.multi_getattr ('bcd') के माध्यम से करें। स्ट्रिंग में डॉट ऑपरेशन की गिनती के बारे में चिंता किए बिना ऑपरेशन को सामान्यीकृत किया जाएगा।

2

मुझे लगता है कि आप जो चाहते हैं उसे हासिल करने का सबसे आसान तरीका operator.attrgetter का उपयोग करना है। `Setattr (क, 'ई.पू.', 2)`:

>>> import operator 
>>> class B(): 
... c = 'foo' 
... 
>>> class A(): 
... b = B() 
... 
>>> a = A() 
>>> operator.attrgetter('b.c')(a) 
'foo' 

विशेषता तो मौजूद नहीं है कि आप एक AttributeError

>>> operator.attrgetter('b.d')(a) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: B instance has no attribute 'd' 
संबंधित मुद्दे