简体   繁体   中英

Why is getattr() not working like I think it should? I think this code should print 'sss'

the next is my code:

class foo:
    def __init__(self):
        self.a = "a"
    def __getattr__(self,x,defalut):
        if x in self:
            return x
        else:return defalut

a=foo()
print getattr(a,'b','sss')

i know the __getattr__ must be 2 argument,but i want to get a default attribute if the attribute is no being.

how can i get it, thanks


and

i found if defined __setattr__ ,my next code is also can't run

class foo:
    def __init__(self):
        self.a={}
    def __setattr__(self,name,value):
            self.a[name]=value

a=foo()#error ,why

hi alex, i changed your example:

class foo(object):
    def __init__(self):
        self.a = {'a': 'boh'}
    def __getattr__(self, x):
        if x in self.a:
            return self.a[x]
        raise AttributeError

a=foo()
print getattr(a,'a','sss')

it print {'a': 'boh'},not 'boh' i think it will print self.a not self.a['a'], This is obviously not want to see

why ,and Is there any way to avoid it

You are confusing the getattr built-in function, which retrieves some attribute binding of an object dynamically (by name), at runtime, and the __getattr__ method, which is invoked when you access some missing attribute of an object.

You can't ask

if x in self:

from within __getattr__ , because the in operator will cause __getattr__ to be invoked, leading to infinite recursion.

If you simply want to have undefined attributes all be defined as some value, then

def __getattr__(self, ignored):
    return "Bob Dobbs"

Your problem number one: you're defining an old-style class (we know you're on Python 2.something, even though you don't tell us, because you're using print as a keyword;-). In Python 2:

class foo:

means you're defining an old-style, aka legacy, class, whose behavior can be rather quirky at times. Never do that -- there's no good reason! The old-style classes exist only for compatibility with old legacy code that relies on their quirks (and were finally abolished in Python 3). Use new style classes instead:

class foo(object):

and then the check if x in self: will not cause a recursive __getattr__ call. It will however cause a failure anyway, because your class does not define a __contains__ method and therefore you cannot check if x is contained in an instance of that class.

If what you're trying to do is whether x is defined in the instance dict of self , don't bother: __getattr__ doesn't even get called in that case -- it's only called when the attribute is not otherwise found in self .

To support three-arguments calls to the getattr built-in, just raise AttributeError in your __getattr__ method if necessary (just as would happen if you had no __getattr__ method at all), and the built-in will do its job (it's the built-in 's job to intercept such cases and return the default if provided). That's the reason one never ever calls special methods such as __getattr__ directly but rather uses built-ins and operators which internally call them -- the built-ins and operators provide substantial added value.

So to give an example which makes somewhat sense:

class foo(object):
    def __init__(self):
        self.blah = {'a': 'boh'}
    def __getattr__(self, x):
        if x in self.blah:
            return self.blah[x]
        raise AttributeError

a=foo()
print getattr(a,'b','sss')

This prints sss , as desired.

If you add a __setattr__ method, that one intercepts every attempt to set attributes on self -- including self.blah = whatever. So -- when you need to bypass the very __setattr__ you're defining -- you must use a different approach. For example:

class foo(object):
    def __init__(self):
        self.__dict__['blah'] = {}
    def __setattr__(self, name, value):
        self.blah[name] = value
    def __getattr__(self, x):
        if x in self.blah:
            return self.blah[x]
        raise AttributeError

a=foo()
print getattr(a,'b','sss')

This also prints sss . Instead of

        self.__dict__['blah'] = {}

you could also use

        object.__setattr__(self, 'blah', {})

Such "upcalls to the superclass's implementation" (which you could also obtain via the super built-in) are one of the rare exceptions to the rules "don't call special methods directly, call the built-in or use the operator instead" -- here, you want to specifically bypass the normal behavior, so the explicit special-method call is a possibility.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM