How to delete property? - python

class C():
#property
def x(self):
return 0
delattr(C(), 'x')
>>> AttributeError: can't delete attribute
I'm aware del C.x works, but this deletes the class's property; can a class instance's property be deleted?

Refer to this answer; TL;DR, it's not about properties, but bound attributes, and x is bound to the class, not the instance, so it cannot be deleted from an instance when an instance doesn't have it in the first place. Demo:
class C():
pass
#property
def y(self):
return 1
c = C()
c.y = y
del c.y # works
c.y
>>> AttributeError: 'C' object has no attribute 'y'

I'm aware del C.x works, but this deletes the class's property; can a class instance's property be deleted?
There's no such thing. Properties are defined on the class, there is nothing on the instance in the example you provide. It's like a method, a method is an attribute of the class which Python execute in the context of the instance.

I got the same error below:
AttributeError: can't delete attribute
When trying to delete the instance variable name with del as shwon below:
class Person:
def __init__(self, name):
self._name = name
#property
def name(self):
return self._name
#name.setter
def name(self, name):
self._name = name
obj = Person("John")
print(hasattr(obj, "name"))
del obj.name # Here
print(hasattr(obj, "name"))
So, I added #name.deleter method as shown below:
class Person:
def __init__(self, name):
self._name = name
#property
def name(self):
return self._name
#name.setter
def name(self, name):
self._name = name
#name.deleter # Here
def name(self):
del self._name
obj = Person("John")
print(hasattr(obj, "name"))
del obj.name # Here
print(hasattr(obj, "name"))
Then, I could delete the instance variable name with del as shown below:
True
False

You can do something like this to delete attr from instance.
https://stackoverflow.com/a/36931502/12789671
class C:
def __init__(self):
self._x: int = 0
#property
def x(self):
return self._x
#x.deleter
def x(self):
delattr(self, "_x")
obj = C()
delattr(obj, "x")
try:
print(obj.x)
except AttributeError:
print("failed to print x")
print(C().x)
failed to print x
0

Related

Can not make property and __getattr__ working together

I am working on a python class that has declared properties, and in which I want to add extra attributes at object instanciation (passed in the init method).
I want them to be read and written.
Finally, I don't want the user to be able to declare custom attributes; it should raise an Error.
class Person:
__slots__ = ["_name", "__dict__"]
def __init__(self, name, extra_arg):
self.__dict__[extra_arg] = None
self._name = name
#property
def name(self):
return self._name
#name.setter
def name(self, value):
self._name = value
def __getattr__(self, item):
if item in self.__dict__:
return self.__dict__[item]
raise AttributeError(item)
person = Person("gribouille", "hello")
person.custom_attribute = value # I want to prevent this
In this example, I can't manage to prevent new attributes to be declared.
When I override setattr method, it seems to collide with my property and I can't manage to retrieve my "name" attribute.
How about checking for existing attributes via hasattr and __slots__?
class Person:
__slots__ = ["_name", "__dict__"]
def __init__(self, name, extra_arg):
self.__dict__[extra_arg] = None
self._name = name
#property
def name(self):
return self._name
#name.setter
def name(self, value):
self._name = value
def __getattr__(self, item):
if item in self.__dict__:
return self.__dict__[item]
raise AttributeError(item)
def __setattr__(self, attr_name, attr_value):
if not (hasattr(self, attr_name) or attr_name in self.__slots__):
raise AttributeError(attr_name)
super().__setattr__(attr_name, attr_value)
person = Person("gribouille", "hello")
person.name = "test"
person.custom_attribute = None # Now: AttributeError: custom_attribute
person.custom_attribute = value # I want to prevent this
To achieve this your class should do NOT have __dict__ attribute, that is __slots__ must not contain __dict__. Consider following simple example
class C1:
__slots__ = ["__dict__"]
class C2:
__slots__ = ["x","y"]
c1 = C1()
c1.custom = "hello"
print(c1.custom) # hello
c2 = C2()
c2.x = 10
c2.y = 30
print(c2.x,c2.y) # 10 30
c2.z = 100 # cause AttributeError: 'C2' object has no attribute 'z'

How to create a property for the property from parent class in python

How can I set a property attribution from child class to a property attribution of parent class? for attribution, I know I can do something like
setattr(self.name, 'nickname', object). However, if I have one class like Animal that is inherited by Bird and include one property called name. Is it possible for me to create another property
under name for class Bird?
class Animal:
def __init__(self):
self._name = None
#property
def name(self):
return self._name
#name.setter
def name(self, value):
self._name = value
class Bird(Animal):
def __init__(self):
super().__init__()
# I need to create the other property under name attribution from Animal class as nickname
#so I can access as cat.name.nickname = 'i am nickname'
#print(cat.name.nickname) # 'i am nickname
##property
#def nickname(self):
# return self._name
#
##name.setter
#def name(self, value):
# self._name = value
cat = Animal()
cat.name = 'i am cat'
print(cat.name) # i am cat
Properties getters and setters can call the property methods on the superclass, with super -
This mean you can recreate the name property in the subclass, retrieve the super-class value, for compatibility, and wrap it on another class, which has the attributes you want.
However, the key _name would be taken in the instance dictionary to keep the value Animal.name property knows about - so we need another shadow name in the instance to keep the values for exclusive of the subclass.
That said, it is still needed to build a clever class that can wrap the original value of the property on the superclass, and know how to handle attribute setting and retrieval on the subclass - the Wrapper code bellow can do that:
class Wrapper(str):
def __new__(cls, original_str, *args):
return super().__new__(cls, original_str)
def __init__(self, original_str, name_in_parent, parent):
self._name = name_in_parent
self._parent = parent
# original_str is taken care of in `__new__`
def __setattr__(self, attrname, value):
if attrname.startswith("_"):
return super().__setattr__(attrname, value)
ns = getattr(self._parent, self._name, None)
if ns is None:
ns = {}
setattr(self._parent, self._name, ns)
ns[attrname] = value
def __getattr__(self, attrname):
return getattr(self._parent, self._name)[attrname]
And this will work with a simple property on the superclass like:
class Animal:
#property
def name(self):
return self._name
#name.setter
def name(self, value):
# just so that the property is not 100% meaningless
self._name = value.lower()
class Bird(Animal):
#property
def name(self):
return Wrapper(super().name, "_bird_name", self)
#name.setter
def name(self, value):
# this turned out to be the trickiest part - to retrieve
# the original property on the superclass so that we can
# call it's setter. `super()` did not work for this.
# We set just the core value - the specialized class
# with more attributes is only used upon reading the property back
super_property = [getattr(val, "name") for val in a.__class__.__mro__[1:] if hasattr(val, "name")][0]
super_property.__set__(self, value)
And this working:
In [511]: b = Bird()
In [512]: b.name = "Woodpecker"
In [513]: b.name
Out[513]: 'woodpecker'
In [514]: b.name.nickname = "Woody"
In [515]: b.__dict__
Out[515]: {'_name': 'woodpecker', '_bird_name': {'nickname': 'Woody'}}
In [516]: b.name.nickname
Out[516]: 'Woody'
If you want to restrict the accepted sub-attributes, just use plain if statements in Wrapper.__setattr__.

Python unit testing Class properties

I am trying to figure out if there's a way to (unit test) verify that the property and the setter is actually called to set the name attribute.
class DummyName:
def __init__(self):
self.name = ''
#property
def name(self):
return self.name
#name.setter
def name(self, name):
if not isinstance(name, basestring):
raise Exception('Name must be a string.')
self.name = name
Trying to do something like this...
#mock.patch.object(DummyName, 'name', new_callable=PropertyMock)
def testNameProperty(self, mock_name):
MockName = Mock()
mock_name.return_value = MockName
dummyName = DummyName()
dummyName.name = 'test_name'
# assert setter is called to set the name
# assert name is called to get the name
# assert name is 'test_name'
Seems like name() and setter are never accessed. the Anyone has a better idea? Thanks!
By using mocks like that you've overwritten the code you're trying to test. Mocks are for calls that are external to the code under test.
An appropriate test for this code is to assert that the exception is raised if you pass something that isn't a string.
def testNameProperty(self):
dummyName = DummyName()
with self.assertRaises(Exception):
dummyName.name = 12345
Your class needs to inherit from object.
class DummyName(object):
def __init__(self):
self._name = ''
#property
def name(self):
return self._name
#name.setter
def name(self, name):
if not isinstance(name, basestring):
raise Exception('Name must be a string.')
self._name = name
You also need to use different variables for the name inside the class, or you'll hit maximum recursion.

How to determine the class of a descriptor?

In this example code, I would like to determine if x is an instance of TestProperty:
class TestProperty(object):
def __init__(self, name):
self._name = name
def __get__(self, instance, cls):
return getattr(instance, self._name)
def __set_(self, instance, value):
setattr(instance, self._name, value)
class Test(object):
x = TestProperty("x")
print isinstance(Test.x, TestProperty)
However, I get the following exception:
Traceback (most recent call last):
File "/home/zenoss/testproperties.py", line 14, in <module>
print isinstance(Test.x, TestProperty)
File "/home/zenoss/testproperties.py", line 6, in __get__
return getattr(instance, self._name)
AttributeError: 'NoneType' object has no attribute 'x'
Is there anyway to tell if an attribute is an instance of a class when it is a descriptor?
With the current __get__, Test.x causes the AttributeError because when the code accessing the descriptor using class, instance is passed None; (=> getattr(None, 'x') => None.x)
You should modify __get__ to handle such case:
>>> class TestProperty(object):
... def __init__(self, name):
... self._name = name
... def __get__(self, instance, cls):
... if instance is None: # To handle access through class, not instance
... return self # returns the TestProperty instance itself.
... return getattr(instance, self._name)
... def __set_(self, instance, value):
... setattr(instance, self._name, value)
...
>>> class Test(object):
... x = TestProperty("x")
...
>>> isinstance(Test.x, TestProperty)
True
BTW, as you may know, with x = TestProperty("x"), accessing x attribute through an instance will cause another exception, because it will call the __get__ (-> getattr(..) -> __get__ -> getattr(..) -> ...) recursively until stack overflow.
The best way to implement a property is with the #property decorator:
class TestProperty(object):
def __init__(self, name):
self._name = name
#property
def name(self):
"""Getter for '_name'."""
return self._name
#name.setter
def name(self, value):
"""Setter for '_name'."""
self._name = value
class Test(object):
x = TestProperty("x")
print(isinstance(Test.x, TestProperty))
It returns True when I run it.
See the documentation for #property at https://docs.python.org/3/library/functions.html#property.

How to access member function as member variable in python classes?

I've got a class:
class Foo():
def bar(name):
return something
and i want to have
foo = Foo()
foo.name
returning the same as foo.bar('name'). Is it possible?
This will automatically use bar if the attribute doesn't exist:
class Foo(object):
def bar(self, name):
return name
def bar2(self, attr, value):
print attr, value
def __getattr__(self, attr):
return self.bar(attr)
def __setattr__(self, attr, value):
self.bar2(attr, value)
foo = Foo()
print foo.name
foo.name = 'not name'
As you describe it now, name only exists in the scope of the function bar and thus Foo has no direct access to it unless you store it in its scope. The simplest solution would be to make a member field called name and set its value in bar.
class Foo():
def __init__(self):
self.name = None
def bar(self, name):
self.name = name
return something
Declare your class:
class Foo:
def bar(self, name):
return 'something'
You can assign the method to an attribute.
foo = Foo()
foo.name = foo.bar
If you prefer you can do it in constructor.
Your class should have an attribute to store the name.
Try this out
#!/usr/bin/python
class Foo:
def __init__(self):
self.name = ''
def bar(self, name):
self.name = name
return "name via bar(): %s" % name
f = Foo()
print f.bar("Jackson")
print f.name

Categories

Resources