I implemented a write-only property in my class with @property. The weird thing is that hasattr behaves differently on the class and corresponding instance with this property.
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
class User(Base, UserMixin):
# codes omitted...
@property
def password(self):
raise AttributeError("password is a write-only attribute!")
@password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
In [6]: hasattr(User,'password')
Out[6]: True
In [7]: u1=User()
In [9]: hasattr(u1,'password')
Out[9]: False
In [12]: getattr(User,'password')
Out[12]: <property at 0x1118a84a8>
In [13]: getattr(u1,'password')
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-13-b1bb8901adc7> in <module>
----> 1 getattr(u1,'password')
~/workspace/python/flask_web_development/fisher/app/models.py in password(self)
82 @property
83 def password(self):
---> 84 raise AttributeError("password is a write-only attribute!")
85
86 @password.setter
AttributeError: password is a write-only attribute!
From the result of getattr, getattr(u1, 'password') tries to execute the method and raises an error, while getattr(User, 'password') doesn't execute the @property method. Why do they behave differently?
Properties are descriptors.
Regarding
getattr:When you access an attribute via
getattror the dot-notation on an object (u1) and the class of that object (User) happens to have a descriptor going by the name you are trying to access, that descriptor's__get__method is called1, as happens when you issuegetattr(u1, 'password'). In your specific case, the logic you defined in your getter (raising theAttributeError) will be executed.With
getattr(User, 'password')the instance passed to the__get__method isNone, in which case__get__just returns the descriptor itself instead of executing the getter logic you implemented.1There are some special rules depending on whether you have a data or a non-data descriptor, as explained in the Descriptor HowTo.
Regarding
hasattr:hasattr(u1, 'password')returnsFalsebecausegetattr(u1, 'password')raises an error. See this question.