Create property with no implemented setter - python

class Human:
def __init__(self) -> None:
self.name = None # type: str
def introduce(self):
print("I'm " + self.name)
class Alice(Human):
def __init__(self) -> None:
super().__init__()
self.name = "Alice"
class Bob(Human):
def __init__(self, rude: bool) -> None:
super().__init__()
self.rude = rude
#property
def name(self) -> str:
return "BOB!" if self.rude else "Bob"
if __name__ == '__main__':
alice = Alice()
alice.introduce()
bob = Bob(rude=True)
bob.introduce()
In the code above, there is an abstract Human class (in reality it is not a human and has more complex methods, not related to the problem). Most of its implementations would set their names by simply assigning a string to the name attribute (just as Alice). But there are few exceptions, like Bob, when there is more complex logic assigned (the value depends on the object state in the moment of resolving).
Therefore in Bob class I created a custom getter for the name property. But as an effect, it is impossible to create a class instance, because invoking the superconstructor results in the following error.
AttributeError: can't set attribute
And it is impossible to add a naive setter as well.
#name.setter
def name(self, name: str):
self.name = name
Why? Because it would result in an infinite loop. How to solve that issue?

why not make a dummy setter
#name.setter
def name(self, value):
pass
When self.name = None is executed it will call this setter and actually do nothing

If you're certain that your subclasses will assign name, then you can leave out the assignment in the parent constructor. Right now, Human is attempting to set to name, when there is no setter. If you removed it from the Human constructor, then Human can look like this:
class Human:
def introduce(self):
print("I'm " + self.name)

For class BobI would have used something like this in this case:
#name.setter
def name(self, name: str):
self._name = name
Afterwards you can do whatever you want in the more complex getter with the internal value. Or did I get the question wrong?
Executing the code would give:
I'm Alice
I'm BOB!

Related

Can I pass the name of an attribute when it itself is being initialised?

I have a class in which I am defining several class attributes as objects. The name of the attribute itself is one of the arguments that need to be passed to the object. Currently they are written manually as in the below example:
class Beatles:
john = Guitar(name='john')
paul = Bass(name='paul')
george = Guitar(name='george')
ringo = Drums(name='ringo')
class Musician:
def __init__(self)
class Guitar(Musician):
def __init__(self, name)
class Bass(Musician):
def __init__(self, name)
class Drums(Musician):
def __init__(self, name)
I have found one work around that uses a metaclass to build the objects using the namespace dictionary of the Foo object:
class Meta(type):
def __new__(cls, name, bases, dict_):
for k, v in dict_.items():
if isinstance(v, Musician):
dict_[k] = type(v)(k)
return super().__new__(cls, name, bases, dict_)
This works only if the 'name' arguments are the only arguments which isn't going to be the case. Is there a better way?
In Python 3.6, there is a new feature to address this pattern, and diminish the need for metaclasses, as those almost always lead to confusion.
All that is needed is that the classes of the objects that are to be attributes have a __set_name__ method (in your case, those are all subclasses of Musician, so, all you have to do is to add a def __set_name__(self, owner, name): ... method to it).
So, all that is needed in your case is:
class Musician:
def __init__(self, ...):
# no need to get a 'name' parameter here
...
# This is called automatically by Python:
def __set_name__(self, owner, name):
# 'owner' is the class object where the attributes are defined in.
# in this case, "Beatles". It is usually not needed, but available.
self.name = name
class Guitar(Musician):
pass
class Bass(Musician):
pass
class Drums(Musician):
pass
class Beatles(metaclass=Meta):
john = Guitar()
paul = Bass()
george = Guitar()
ringo = Drums()
Now, if for some reason you want to implement this by using metaclasses (let's say you have to work on Python 3.5, or can't change the code on the Musician classes) - you could use functools.partial to store the other attributes and just pass the missing name attribute in the same metaclass code you have above:
from functools import partial
class Beatles:
john = partial(Guitar, other_attribute='')
paul = partial(Bass, wearing_shoes=False)
george = partial(Guitar)
ringo = partial()
(And keep in mind you can shorten partial for readability if desired, with things as simple as from functools import partial as P )
The object itself doesn't exist yet, but the constructor is, ultimately a function, so you pass what you need as a function argument.
In this case, the example may not be conveying the question you want to ask. You don't have a name because you're a drummer, you have a name because you're a person. As the top level of the hierarchy, Musician would have that property, not Drummer, and it should be a parameter in Musician's constructor:
class Musician:
def __init__(self, name):
self.name = name
class Guitar(Musician):
def __init__(self, name):
super().__init__(name)
class Bass(Musician):
def __init__(self, name):
super().__init__(name)
class Drums(Musician):
def __init__(self, name):
super().__init__(name)
Beatles = {
Guitar(name='john'),
Bass(name='paul'),
Guitar(name='george'),
Drums(name='ringo'),
}
for m in Beatles:
print (m.name)

What is the difference between readable property method and a callable function that is just returns the data as a property can?

I have a property that returns list of names with "ash" in it
class BaseClass(object):
def __init__(self):
self.filter_key = ""
self.name = ""
def filter_names(self, filter_key):
self.filter_key = filter_key
#property
def student_names(self):
return self.names
def callable_function_names(self):
return names
and then student class that inherits BaseClass
class StudentClass(BaseClass):
#property
def student_names(self):
names = super(StudentClass, self).student_names
return [name for name in names if self.filter_students in name]
#property
def filter_key(self):
"""Gets """
return self.filter_key
#slot_key.setter
def filter_key(self, key):
"""Sets name filter"""
self.filter_names(key)
# or by doing :
def callable_function_names(self):
names = super(StudentClass, self).callable_function_names()
return [name for name in names if self.filter_students in name]
So if I create obj of the student class.
studentclsObj = StudentClass()
studentclsObj.filter_key = "ash"
print studentclsObj.student_names
print studentclsObj.callable_function_names()
I can achieve the same result with both above prints, is there any difference and what is preferred and right way to do ?
One use case of properties is not breaking API. This is one of main strengths of python IMO. You can take a function, make transform it in a callable object, add new functionality without breaking old code, now the property
I see three main uses of properties over attributes,
Read only attributes
Is easy to create read only attributes with properties. They are non verbose, self documenting and simple
class Foo:
def __init__(self, bar):
self._bar = bar
#property
def bar(self):
return self._bar
Validation on writable properties
class Foo:
def __init__(self, bar):
self._bar = bar
#property
def bar(self):
return self._bar
#bar.setter
def bar(self, val):
if valid(val):
self._bar = val
This is a kind of defensive programming
Keep API compatibility
Imagine that you have a class for a bank account, with
a balance property
class BankAccount:
def __init__(self):
self.balance = 0
You have this code and it works fine. But know your client
says, I need you to log every balance lookup. You can replace
the attribute by a property without breaking old code
class BankAccount:
def __init__(self):
self._balance = 0
#property
def balance(self):
self.log_balance_read()
return self._balance
There is no difference between a property and a method which return the same value. Go for the simpler, use method for actions and state changes and attributes for real attributes, if you need to add logic to attribute lookup, python will let you do it

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.

Alternative for inheritance in python

How to save code duplication in the following scenario ?
say Aand B are two classes having a common function(say) name
class A(object):
name = 'foo'
#property
def name(self): # the common function
return self.name
similarly B
class B(object):
name = 'bar'
#property
def name(self):
return self.name
One way would be to make a class from which both of them inherit from, and define name there.
Any good alternatives ?
If you're really determined to avoid inheritance, just define a function outside of either class:
def get_name(object):
return object.name
class A(object):
name = 'foo'
def get_name(self): # the common function
return self.name
class B(A):
pass
In this case B would inherit from A
Is there a reason you can't have B inherit from A?
class B(A):
name = 'bar'
Since you are decorating name with #property, I am assuming you want this to be an instance variable. If you want this to return a more private variable, let's call it _name, you have to do:
class A(object):
def __init__(self):
self._name = 'foo'
#property
def name(self):
return self._name
You can't have both a variable and a function have the same name, since the latter will simply override the former. If you want a base class that takes care of this, it would look like this:
class HasName(object):
def __init__(self, name):
self._name = name
#property
def name(self):
return self._name
class A(HasName):
def __init__(self):
self._name = 'foo'
class B(HasName):
def __init__(self):
self._name = 'bar'
You can also call the constructor in HasName.
Assuming self.name stands in for a more complex method, the easiest way to cut down on duplicated code is to move the function out to the module and have it take an arbitrary object as a parameter. Then, if you still want to tie the method directly to the class, you can add a short method that dispatches to the module function.
def _name(obj):
return obj.name
class A(object):
# ...
#property
def name(self):
return _name(self)
class B(object):
# ...
#property
def name(self):
return _name(self)
Note that this will not work well if A.name and B.name have completely different behaviors. If the _name function starts checking the type of the object given, reconsider whether you really want to abstract that functionality in the first place.

Categories

Resources