Python hasattr vs getattr - python

I have been reading lately some tweets and the python documentation about hasattr and it says:
hasattr(object, name)
The arguments are an object and a string. The result is True if the string is the name of >> one of the object’s attributes, False if not. (This is implemented by calling
getattr(object, name) and seeing whether it raises an AttributeError or not.)
There is a motto in Python that says that is Easier to ask for forgiveness than permission where I usually agree.
I tried to do a performance test in this case with a really simple python code:
import timeit
definition="""\
class A(object):
a = 1
a = A()
"""
stm="""\
hasattr(a, 'a')
"""
print timeit.timeit(stmt=stm, setup=definition, number=10000000)
stm="""\
getattr(a, 'a')
"""
print timeit.timeit(stmt=stm, setup=definition, number=10000000)
With the results:
$ python test.py
hasattr(a, 'a')
1.26515984535
getattr(a, 'a')
1.32518696785
I´ve tried also what happens if the attribute doesn´t exists and the differences between getattr and hasattr are bigger. So what I´ve seen so far is that getattr is slower than hasattr, but in the documentation it says that it calls getattr.
I´ve searched the CPython implementation of hasattr and getattr and it seems that both call the next function:
v = PyObject_GetAttr(v, name);
but there is more boilerplate in getattr than in hasattr that probably makes it slower.
Does anyone knows why in the documentation we say that hasattr calls getattr and we seem to encourage the users to use getattr instead of hasattr when it really isn´t due to performance? Is just because it is more pythonic?
Maybe I am doing something wrong in my test :)
Thanks,
Raúl

The documentation does not encourage, the documentation just states the obvious. The hasattr is implemented as such, and throwing an AttributeError from a property getter can make it look like the attribute does not exist. This is an important detail, and that is why it is explicitly stated in the documentation. Consider for example this code:
class Spam(object):
sausages = False
#property
def eggs(self):
if self.sausages:
return 42
raise AttributeError("No eggs without sausages")
#property
def invalid(self):
return self.foobar
spam = Spam()
print(hasattr(Spam, 'eggs'))
print(hasattr(spam, 'eggs'))
spam.sausages = True
print(hasattr(spam, 'eggs'))
print(hasattr(spam, 'invalid'))
The result is
True
False
True
False
That is the Spam class has a property descriptor for eggs, but since the getter raises AttributeError if not self.sausages, then the instance of that class does not "hasattr" eggs.
Other than that, use hasattr only when you don't need the value; if you need the value, use getattr with 2 arguments and catch the exception, or 3 arguments, the third being a sensible default value.
The results using getattr() (2.7.9):
>>> spam = Spam()
>>> print(getattr(Spam, 'eggs'))
<property object at 0x01E2A570>
>>> print(getattr(spam, 'eggs'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in eggs
AttributeError: No eggs without sausages
>>> spam.sausages = True
>>> print(getattr(spam, 'eggs'))
42
>>> print(getattr(spam, 'invalid'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 10, in invalid
AttributeError: 'Spam' object has no attribute 'invalid'
>>>

Seems that hasattr has a problem with swallowing exceptions (at least in Python 2.7), so probably is better to stay away from it until it's fixed.
Take, for instance, the following code:
>>> class Foo(object):
... #property
... def my_attr(self):
... raise ValueError('nope, nope, nope')
...
>>> bar = Foo()
>>> bar.my_attr
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in my_attr
ValueError: nope, nope, nope
>>> hasattr(Foo, 'my_attr')
True
>>> hasattr(bar, 'my_attr')
False
>>> getattr(bar, 'my_attr', None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in my_attr
ValueError: nope, nope, nope
>>>

Related

Setting attributes on __func__

In the documentation on instance methods it states that:
Methods also support accessing (but not setting) the arbitrary function attributes on the underlying function object.
But I can't seem to be able to verify that restriction. I tried setting both an arbitrary value and one of the "Special Attributes" of functions:
class cls:
def foo(self):
f = self.foo.__func__
f.a = "some value" # arbitrary value
f.__doc__ = "Documentation"
print(f.a, f.__doc__)
When executed, no errors are produced and the output is as expected:
cls().foo() # prints out f.a, f.__doc__
What is it that I'm misunderstanding with the documentation?
You are misunderstanding what is being said. It says that you can access but not set the attributes of the underlying function object from the method!
>>> class Foo:
... def foo(self):
... self.foo.__func__.a = 1
... print(self.foo.a)
... self.foo.a = 2
...
>>> Foo().foo()
1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in foo
AttributeError: 'method' object has no attribute 'a'
Note how foo.a is updated when you set it on the __func__ value, but you cannot set it directly using self.foo.a = value.
So the function object can be modified as you please, the method wrapper only provides read-only access to the attributes on the underlying function.

Does python consider any method without cls or self arguments implicitly as static?

The following are test classes with methods not taking in cls or self arguments and dont have #staticmethod decorator. They work like normal static methods without complaining about arguments. This seems contrary to my understanding of python methods. Does python automatically treat non-class, non-instance methods as static?
>>> class Test():
... def testme(s):
... print(s)
...
>>> Test.testme('hello')
hello
>>> class Test():
... def testme():
... print('no')
...
>>> Test.testme()
no
P.S: I am using python3.4
It sort of does, yes. However, note that if you call such an "implicit static method" on an instance, you will get an error:
>>> Test().testme()
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
Test().testme()
TypeError: testme() takes 0 positional arguments but 1 was given
This is because the self parameter still gets passed, which doesn't happen with a proper #staticmethod:
>>> class Test:
#staticmethod
def testme():
print('no')
>>> Test.testme()
no
>>> Test().testme()
no
Note that this doesn't work in Python 2:
>>> class Test(object):
... def testme():
... print 'no'
...
>>> Test.testme()
Traceback (most recent call last):
File "<ipython-input-74-09d78063da08>", line 1, in <module>
Test.testme()
TypeError: unbound method testme() must be called with Test instance as first argument (got nothing instead)
But in Python 3, unbound methods were removed, as Alex Martelli points out in this answer. So really all you're doing is calling a plain function that happens to be defined inside the Test class.

How to use python-magic 5.19-1

I need to determine MIME-types from files without suffix in python3 and I thought of python-magic as a fitting solution therefor.
Unfortunately it does not work as described here:
https://github.com/ahupp/python-magic/blob/master/README.md
What happens is this:
>>> import magic
>>> magic.from_file("testdata/test.pdf")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'from_file'
So I had a look at the object, which provides me with the class Magic for which I found documentation here:
http://filemagic.readthedocs.org/en/latest/guide.html
I was surprised, that this did not work either:
>>> with magic.Magic() as m:
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() missing 1 required positional argument: 'ms'
>>> m = magic.Magic()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() missing 1 required positional argument: 'ms'
>>>
I could not find any information about how to use the class Magic anywhere, so I went on doing trial and error, until I figured out, that it accepts instances of LP_magic_set only for ms.
Some of them are returned by the module's methods
magic.magic_set() and magic_t().
So I tried to instanciate Magic with either of them.
When I then call the file() method from the instance, it will always return an empty result and the errlvl() method tells me error no. 22.
So how do I use magic anyway?
I think that you are confusing different implementations of "python-magic"
You appear to have installed python-magic-5.19.1, however, you reference firstly the documentation for python-magic-0.4.6, and secondly filemagic-1.6. I think that you are better off using python-magic-0.4.6 as it is readily available at PYPI and easily installed via pip into virtualenv environments.
Documentation for python-magic-5.19.1 is hard to come by, but I managed to get it to work like this:
>>> import magic
>>> m=magic.open(magic.MAGIC_NONE)
>>> m.load()
0
>>> m.file('/etc/passwd')
'ASCII text'
>>> m.file('/usr/share/cups/data/default.pdf')
'PDF document, version 1.5'
You can also get different magic descriptions, e.g. MIME type:
>>> m=magic.open(magic.MAGIC_MIME)
>>> m.load()
0
>>> m.file('/etc/passwd')
'text/plain; charset=us-ascii'
>>> m.file('/usr/share/cups/data/default.pdf')
'application/pdf; charset=binary'
or for more recent versions of python-magic-5.30
>>> import magic
>>> magic.detect_from_filename('/etc/passwd')
FileMagic(mime_type='text/plain', encoding='us-ascii', name='ASCII text')
>>> magic.detect_from_filename('/etc/passwd').mime_type
'text/plain'

About dynamic assignment of attributes in Python 2.x

When I try to dynamically add attributes to instances of object class, I get an AttributeError. However, it is possible do it with instances of subclasses of object.
Does anybody know why?
>>> obj = object()
>>> obj.new_attr = "some value"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'new_attr'
>>> class MyClass(object):
... pass
...
>>> obj = MyClass()
>>> obj.new_attr = "some value"
>>> print obj.new_attr
some value
There is a note in the documentation about that:
http://docs.python.org/3/library/functions.html#object
Note: object does not have a __dict__, so you can’t assign arbitrary attributes to an instance of the object class.
There is also a discussion about this on python mailing list:
https://mail.python.org/pipermail/python-list/2011-October/614249.html

Python: dynamically add attributes to new-style class/obj

Can I dynamically add attributes to instances of a new-style class (one that derives from object)?
Details:
I'm working with an instance of sqlite3.Connection. Simply extending the class isn't an option because I don't get the instance by calling a constructor; I get it by calling sqlite3.connect().
Building a wrapper doesn't save me much of the bulk for the code I'm writing.
Python 2.7.1
Edit
Right answers all. But I still am not reaching my goal; instances of sqlite3.Connection bar my attempts to set attributes in the following ways (as do instances of object itself). I always get an AttributeError:
> conn = sqlite3.connect([filepath])
> conn.a = 'foo'
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
conn.a = 'foo'
AttributeError: 'object' object has no attribute 'a'
> conn.__setattr__('a','foo')
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
conn.__setattr__('a','foo')
AttributeError: 'object' object has no attribute 'a'
Help?
Yes, unless the class is using __slots__ or preventing attribute writing by overriding __setattr__, or an internal Python class, or a Python class implemented natively (usually in C).
You can always try setting an attribute. Except for seriously weird __setattr__ implementations, assigning an attribute to an instance of a class of one of the types mentioned above should raise an AttributeError.
In these cases, you'll have to use a wrapper, like this:
class AttrWrapper(object):
def __init__(self, wrapped):
self._wrapped = wrapped
def __getattr__(self, n):
return getattr(self._wrapped, n)
conn = AttrWrapper(sqlite3.connect(filepath))
Simple experimentation:
In []: class Tst(object): pass
..:
In []: t= Tst()
In []: t.attr= 'is this valid?'
In []: t.attr
Out[]: 'is this valid?'
So, indeed it seems to be possible to do that.
Update:
But from the documentation: SQLite is a C library that ..., so it seems that you really need to wrap it.
conn.a = 'foo',
or any dynamic assignment is valid, if conn is
<type 'classobj'>.
Things like:
c=object()
c.e=1
will raise an Attribute error. On the otherhand: Python allows you to do fantastic Metaclass programming:
>>>from new import classobj
>>>Foo2 = classobj('Foo2',(Foo,),{'bar':lambda self:'bar'})
>>>Foo2().bar()
>>>'bar'
>>>Foo2().say_foo()
>>>foo

Categories

Resources