I'm new to using descriptors and I think I have a good understanding on how they work but I have come across a problem and i'm not sure how to fix it.
Code
class Foo:
class Bar:
def __get__(self,instance, owner):
return 10
def __set__(self,instance,value):
raise Exception
bar=Bar()
print(Foo.bar)
Foo.bar=5
print(Foo.bar)
Output
>>> 10
>>> 5
Im trying to make bar a constant for testing purposes, I know about the property decorator but I prefer using descriptors.
First I print out the value of bar to see if __get__ works - and it does, the output is 10.
But then when I assign 5 to bar the expected result would be an exception but instead what happens is 5 gets assigned to bar despite specifying __set__ so when I print again the second output is 5.
Can someone tell me why the error isn't being raised?
From the docs:
object.__set__(self, instance, value)
Called to set the attribute on an instance instance of the owner class to a new value, value.
In your code, Foo.bar = 5 is setting the class attribute, not an instance attribute. If you do use an instance (without first setting Foo.bar = 5, which overrides your descriptor), then you get an exception as expected:
>>> f = Foo()
>>> f.bar
10
>>> f.bar = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __set__
Exception
If you want the __set__ behaviour to apply when the class attribute is set, then the class itself needs to be an instance of a metaclass which uses the descriptor:
class FooMeta(type):
class Bar:
def __get__(self,instance, owner):
return 10
def __set__(self,instance,value):
raise Exception
bar = Bar()
class Foo(metaclass=FooMeta):
pass
Testing:
>>> Foo.bar
10
>>> Foo.bar = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __set__
Exception
Related
I was wondering about classes and how I can access their values from the outside with either printing it or using the _str__ function. I came across this question:
Python function not accessing class variable
Then I did this test in Shell, but it didn't work as expected. I wonder why the other answer worked, but not this one.
(edit)
My question was answered by how to instantiate a class, not instance variables.
>>> class test:
def __init__(self):
self.testy=0
def __str__(self):
return self.testy
>>> a=test
>>> b=test
>>> print(a)
<class '__main__.test'>
>>> a
<class '__main__.test'>
>>> a.testy
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
a.testy
AttributeError: type object 'test' has no attribute 'testy'
>>>
You had done a mistake while creating objects, please find below differences:
class test:
def __init__(self):
self.testy=0
def __str__(self):
return self.testy
a = test()
b = test()
a.testy
output: 0
What you have done:
c = test
d = test
c.testy
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: type object 'test' has no attribute 'testy'
Explanation:
when you are creating objects for a class use object = class_name()
**https://docs.python.org/3/tutorial/classes.html#class-objects
You define your variable inside init, which is only called when the class is instantiated. For a longer explanation, I'd refer you to the first answer on the question you linked.
I want to provide a method that can be used on a Python 2.7 class object, but does not pollute the attribute namespace of its instances. Is there any way to do this?
>>> class Foo(object):
... #classmethod
... def ugh(cls):
... return 33
...
>>> Foo.ugh()
33
>>> foo = Foo()
>>> foo.ugh()
33
You could subclass the classmethod descriptor:
class classonly(classmethod):
def __get__(self, obj, type):
if obj: raise AttributeError
return super(classonly, self).__get__(obj, type)
This is how it would behave:
class C(object):
#classonly
def foo(cls):
return 42
>>> C.foo()
42
>>> c=C()
>>> c.foo()
AttributeError
This desugars to the descriptor call (rather, it is invoked by the default implementation of __getattribute__):
>>> C.__dict__['foo'].__get__(None, C)
<bound method C.foo of <class '__main__.C'>>
>>> C.__dict__['foo'].__get__(c, type(c))
AttributeError
Required reading: Data Model — Implementing Descriptors and Descriptor HowTo Guide.
ugh is not in the namespace:
>>> foo.__dict__
{}
but the rules for attribute lookup fall back to the type of the instance for missing names. You can override Foo.__getattribute__ to prevent this.
class Foo(object):
#classmethod
def ugh(cls):
return 33
def __getattribute__(self, name):
if name == 'ugh':
raise AttributeError("Access to class method 'ugh' block from instance")
return super(Foo,self).__getattribute__(name)
This produces:
>>> foo = Foo()
>>> foo.ugh()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "tmp.py", line 8, in __getattribute__
raise AttributeError("Access to class method 'ugh' block from instance")
AttributeError: Access to class method 'ugh' block from instance
>>> Foo.ugh()
33
You must use __getattribute__, which is called unconditionally on any attribute access, rather than __getattr__, which is only called after the normal lookup (which includes checking the type's namespace) fails.
Python has quasi-private variables that use name-munging to reduce accidental access. Methods and object variables of the form __name are converted to _ClassName__name. Python automatically changes the name when compiling methods on the class but doesn't change the name for subclasses.
I can use the private method in a class
>>> class A(object):
... def __private(self):
... print('boo')
... def hello(self):
... self.__private()
...
>>>
>>> A().hello()
boo
But not outside the class
>>> A().__private()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute '__private'
>>>
Or in subclasses
>>> class B(A):
... def hello2(self):
... self.__private()
...
>>>
>>> B().hello()
boo
>>> B().hello2()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in hello2
AttributeError: 'B' object has no attribute '_B__private'
Yes, you can create the method in the metaclass.
class FooMeta(type):
# No #classmethod here
def ugh(cls):
return 33
class Foo(object):
__metaclass__ = FooMeta
Foo.ugh() # returns 33
Foo().ugh() # AttributeError
Note that metaclasses are a power feature, and their use is discouraged if unnecessary. In particular, multiple inheritance requires special care if the parent classes have different metaclasses.
Let's say a function looks at an object and checks if it has a function a_method:
def func(obj):
if hasattr(obj, 'a_method'):
...
else:
...
I have an object whose class defines a_method, but I want to hide it from hasattr. I don't want to change the implementation of func to achieve this hiding, so what hack can I do to solve this problem?
If the method is defined on the class you appear to be able to remove it from the __dict__ for the class. This prevents lookups (hasattr will return false). You can still use the function if you keep a reference to it when you remove it (like the example) - just remember that you have to pass in an instance of the class for self, it's not being called with the implied self.
>>> class A:
... def meth(self):
... print "In method."
...
>>>
>>> a = A()
>>> a.meth
<bound method A.meth of <__main__.A instance at 0x0218AB48>>
>>> fn = A.__dict__.pop('meth')
>>> hasattr(a, 'meth')
False
>>> a.meth
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'meth'
>>> fn()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: meth() takes exactly 1 argument (0 given)
>>> fn(a)
In method.
You could redefine the hasattr function. Below is an example.
saved_hasattr = hasattr
def hasattr(obj, method):
if method == 'MY_METHOD':
return False
else:
return saved_hasattr(obj, method)
Note that you probably want to implement more detailed checks than just checking the method name. For example checking the object type might be beneficial.
Try this:
class Test(object):
def __hideme(self):
print 'hidden'
t = Test()
print hasattr(t,"__hideme") #prints False....
I believe this works b/c of the double underscore magic of hiding members (owning to name mangling) of a class to outside world...Unless someone has a strong argument against this, I'd think this is way better than popping stuff off from __dict__? Thoughts?
I'm trying to set a Python class property outside of the class via the setattr(self, item, value) function.
class MyClass:
def getMyProperty(self):
return self.__my_property
def setMyProperty(self, value):
if value is None:
value = ''
self.__my_property = value
my_property = property( getMyProperty, setMyProperty )
And in another script, I create an instance and want to specify the property and let the property mutator handle the simple validation.
myClass = MyClass()
new_value = None
# notice the property in quotes
setattr(myClass, 'my_property', new_value)
The problem is that it doesn't appear to be calling the setMyProperty(self, value) mutator. For a quick test to verify that it doesn't get called, I change the mutator to:
def setMyProperty(self, value):
raise ValueError('WTF! Why are you not being called?')
if value is None:
value = ''
self.__my_property = value
I'm fairly new to Python, and perhaps there's another way to do what I'm trying to do, but can someone explain why the mutator isn't being called when setattr(self, item, value) is called?
Is there another way to set a property via a string? I need the validation inside the mutator to be executed when setting the property value.
Works for me:
>>> class MyClass(object):
... def get(self): return 10
... def setprop(self, val): raise ValueError("hax%s"%str(val))
... prop = property(get, setprop)
...
>>> i = MyClass()
>>> i.prop =4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in setprop
ValueError: hax4
>>> i.prop
10
>>> setattr(i, 'prop', 12)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in setprop
ValueError: hax12
The code you pasted seems to do the same as mine, except that my class inherits from object, but that's cause I'm running Python 2.6 and I thought that in 2.7 all classes automatically inherit from object. Try that, though, and see if it helps.
To make it even clearer: try just doing myClass.my_property = 4. Does that raise an exception? If not then it's an issue with inheriting from object - properties only work for new-style classes, i.e. classes that inherit from object.
Consider such code:
class A ():
name = 7
description = 8
color = 9
class B(A):
pass
Class B now has (inherits) all attributes of class A. For some reason I want B not to inherit attribute 'color'. Is there a possibility to do this?
Yes, I know, that I can first create class B with attributes 'name' and 'description' and then inherit class A from B adding attribute 'color'. But in my exact case, B is actually a reduced version of A, so for me it seems more logical to remove attribute in B (if possible).
I think the best solution would be to change your class hierarchy so you can get the classes you want without any fancy tricks.
However, if you have a really good reason not to do this you could hide the color attribute using a Descriptor. You'll need to be using new style classes for this to work.
class A(object):
name = 7
description = 8
color = 9
class Hider(object):
def __get__(self,instance,owner):
raise AttributeError, "Hidden attribute"
def __set__(self, obj, val):
raise AttributeError, "Hidden attribute"
class B(A):
color = Hider()
You'll then get an AttributeError when you try to use the color attribute:
>>> B.color
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __get__
AttributeError: Hidden attribute
>>> instance = B()
>>> instance.color
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __get__
AttributeError: Hidden attribute
>>> instance.color = 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __set__
AttributeError: Hidden attribute
You can supply a different value for color in B, but if you want B not to have some property of A then there's only one clean way to do it: create a new base class.
class Base():
name = 7
description = 8
class A(Base):
color = 9
class B(Base):
pass