Alternative for inheritance in python - 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.

Related

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

Python Class Name as Class Variable

I'm working as an application with classes and subclasses. For each class, both super and sub, there is a class variable called label. I would like the label variable for the super class to default to the class name. For example:
class Super():
label = 'Super'
class Sub(Super):
label = 'Sub'
Rather than manually type out the variable for each class, is it possible to derive the variable from the class name in the super class and have it automatically populated for the subclasses?
class Super():
label = # Code to get class name
class Sub(Super)
pass
# When inherited Sub.label == 'Sub'.
The reason for this is that this will be the default behavior. I'm also hoping that if I can get the default behavior, I can override it later by specifying an alternate label.
class SecondSub(Super):
label = 'Pie' # Override the default of SecondSub.label == 'SecondSub'
I've tried using __name__, but that's not working and just gives me '__main__'.
I would like to use the class variable label in #classmethod methods. So I would like to be able to reference the value without having to actually create a Super() or Sub() object, like below:
class Super():
label = # Magic
#classmethod
def do_something_with_label(cls):
print(cls.label)
you can return self.__class__.__name__ in label as a property
class Super:
#property
def label(self):
return self.__class__.__name__
class Sub(Super):
pass
print Sub().label
alternatively you could set it in the __init__ method
def __init__(self):
self.label = self.__class__.__name__
this will obviously only work on instantiated classes
to access the class name inside of a class method you would need to just call __name__ on the cls
class XYZ:
#classmethod
def my_label(cls):
return cls.__name__
print XYZ.my_label()
this solution might work too (snagged from https://stackoverflow.com/a/13624858/541038)
class classproperty(object):
def __init__(self, fget):
self.fget = fget
def __get__(self, owner_self, owner_cls):
return self.fget(owner_cls)
class Super(object):
#classproperty
def label(cls):
return cls.__name__
class Sub(Super):
pass
print Sub.label #works on class
print Sub().label #also works on an instance
class Sub2(Sub):
#classmethod
def some_classmethod(cls):
print cls.label
Sub2.some_classmethod()
You can use a descriptor:
class ClassNameDescriptor(object):
def __get__(self, obj, type_):
return type_.__name__
class Super(object):
label = ClassNameDescriptor()
class Sub(Super):
pass
class SecondSub(Super):
label = 'Foo'
Demo:
>>> Super.label
'Super'
>>> Sub.label
'Sub'
>>> SecondSub.label
'Foo'
>>> Sub().label
'Sub'
>>> SecondSub().label
'Foo'
If class ThirdSub(SecondSub) should have ThirdSub.label == 'ThirdSub' instead of ThirdSub.label == 'Foo', you can do that with a bit more work. Assigning label at the class level will be inherited, unless you use a metaclass (which is a lot more hassle than it's worth for this), but we can have the label descriptor look for a _label attribute instead:
class ClassNameDescriptor(object):
def __get__(self, obj, type_):
try:
return type_.__dict__['_label']
except KeyError:
return type_.__name__
Demo:
>>> class SecondSub(Super):
... _label = 'Foo'
...
>>> class ThirdSub(SecondSub):
... pass
...
>>> SecondSub.label
'Foo'
>>> ThirdSub.label
'ThirdSub'
A metaclass might be useful here.
class Labeller(type):
def __new__(meta, name, bases, dct):
dct.setdefault('label', name)
return super(Labeller, meta).__new__(meta, name, bases, dct)
# Python 2
# class Super(object):
# __metaclass__ = Labeller
class Super(metaclass=Labeller):
pass
class Sub(Super):
pass
class SecondSub(Super):
label = 'Pie'
class ThirdSub(SecondSub):
pass
Disclaimer: when providing a custom metaclass for your class, you need to make sure it is compatible with whatever metaclass(es) are used by any class in its ancestry. Generally, this means making sure your metaclass inherits from all the other metaclasses, but it can be nontrivial to do so. In practice, metaclasses aren't so commonly used, so it's usually just a matter of subclassing type, but it's something to be aware of.
As of Python 3.6, the cleanest way to achieve this is with __init_subclass__ hook introduced in PEP 487. It is much simpler (and easier to manage with respect to inheritance) than using a metaclass.
class Base:
#classmethod
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
if 'label' not in cls.__dict__: # Check if label has been set in the class itself, i.e. not inherited from any of its superclasses
cls.label = cls.__name__ # If not, default to class's __name__
class Sub1(Base):
pass
class Sub2(Base):
label = 'Custom'
class SubSub(Sub2):
pass
print(Sub1.label) # Sub1
print(Sub2.label) # Custom
print(SubSub.label) # SubSub

Create python property function dynamically

I want to create property method dynamically.
It means, for example, i want first class A to be equivalent with second one.
Is there any way?
class A(object):
def __init__(self):
self._a = 10
self.createPropertyMethod(a, self._a, getFunc, setFunc, delFunc)
def createPropertyMethod(self, name, value, getFunc, setFunc, delFunc)
#TODO
.
class A(object):
def __init__(self):
self._a = 10
#property
def a(self):
return getFunc(self._a)
#a.setter
def a(self, value):
setFunc(self._a, value)
#a.deleter
def a(self):
delFunc(self._a)
You can't, not on instances. The property object needs to be part of the class for the descriptor protocol on which it relies to work.
You can use the __getattr__, __setattr__ and __delattr__ hooks instead to proxy attribute access dynamically.

create instance of class within class definition

I'm trying to have a default instance of a class. I want to have
class Foo():
def __init__(self):
....
_default = Foo()
#staticmethod
def get_default():
return _default
However _default = Foo() leads to NameError: name 'Foo' is not defined
Foo does not exist until the class definition is finalized. You can easily refer to it after the class definition, though:
class Foo(object):
def __init__(self):
# ....
Foo.default_instance = Foo()
Note also that I have removed the superfluous getter method in favor of a plain old attribute.
You can also solve the problem with a decorator:
def defaultinstance(Class):
Class.default_instance = Class()
return Class
#defaultinstance
class Foo(object):
# ...
Or, gratuitously, with a metaclass:
def defaultmeta(name, bases, attrs):
Class = type(name, bases, attrs)
Class.default_instance = Class()
return Class
# Python 2.x usage
class Foo(object):
__metaclass__ = defaultmeta
# ...
# Python 3.x usage
class Foo(metaclass=defaultmeta):
# ...
When might you might want to use each method?
Use the post-definition class attribute assignment for one-offs
Use the decorator if you want the same behavior in a lot of unrelated classes and to "hide" the implementation of it (it's not really hidden, or even that complicated, here, though)
Use the metaclass if you want the behavior to be inheritable in which case it's not really gratuitous. :-)
You cannot refer to a class that doesn't yet exist. Within the class definition body, the Foo class is not yet created.
Add the attribute after the class has been created:
class Foo():
def __init__(self):
....
#staticmethod
def get_default():
return Foo._default
Foo._default = Foo()
Note that you also need to alter the get_default() static method; the class body doesn't form a scope, so you cannot reach _default as a non-local from get_default().
You are now, however, repeating yourself a lot. Reduce repetition a little by making get_default() a classmethod instead:
class Foo():
def __init__(self):
....
#classmethod
def get_default(cls):
return cls._default
Foo._default = Foo()
or create the default on first call:
class Foo():
def __init__(self):
....
#classmethod
def get_default(cls):
if not hasattr(cls, '_default'):
cls._default = cls()
return cls._default
You may lazily initialize your default instance.
class Foo(object):
_default = None
#staticmethod
def get_default():
if not Foo._default:
Foo._default = Foo()
return Foo._default

How to call a property of the base class if this property is being overwritten in the derived class?

I'm changing some classes of mine from an extensive use of getters and setters to a more pythonic use of properties.
But now I'm stuck because some of my previous getters or setters would call the corresponding method of the base class, and then perform something else. But how can this be accomplished with properties? How to call the property getter or setter in the parent class?
Of course just calling the attribute itself gives infinite recursion.
class Foo(object):
#property
def bar(self):
return 5
#bar.setter
def bar(self, a):
print a
class FooBar(Foo):
#property
def bar(self):
# return the same value
# as in the base class
return self.bar # --> recursion!
#bar.setter
def bar(self, c):
# perform the same action
# as in the base class
self.bar = c # --> recursion!
# then do something else
print 'something else'
fb = FooBar()
fb.bar = 7
You might think you could call the base class function which is called by property:
class FooBar(Foo):
#property
def bar(self):
# return the same value
# as in the base class
return Foo.bar(self)
Though this is the most obvious thing to try I think - it does not work because bar is a property, not a callable.
But a property is just an object, with a getter method to find the corresponding attribute:
class FooBar(Foo):
#property
def bar(self):
# return the same value
# as in the base class
return Foo.bar.fget(self)
super() should do the trick:
return super().bar
In Python 2.x you need to use the more verbose syntax:
return super(FooBar, self).bar
There is an alternative using super that does not require to explicitly reference the base class name.
Base class A:
class A(object):
def __init__(self):
self._prop = None
#property
def prop(self):
return self._prop
#prop.setter
def prop(self, value):
self._prop = value
class B(A):
# we want to extend prop here
pass
In B, accessing the property getter of the parent class A:
As others have already answered, it's:
super(B, self).prop
Or in Python 3:
super().prop
This returns the value returned by the getter of the property, not the getter itself but it's sufficient to extend the getter.
In B, accessing the property setter of the parent class A:
The best recommendation I've seen so far is the following:
A.prop.fset(self, value)
I believe this one is better:
super(B, self.__class__).prop.fset(self, value)
In this example both options are equivalent but using super has the advantage of being independent from the base classes of B. If B were to inherit from a C class also extending the property, you would not have to update B's code.
Full code of B extending A's property:
class B(A):
#property
def prop(self):
value = super(B, self).prop
# do something with / modify value here
return value
#prop.setter
def prop(self, value):
# do something with / modify value here
super(B, self.__class__).prop.fset(self, value)
One caveat:
Unless your property doesn't have a setter, you have to define both the setter and the getter in B even if you only change the behaviour of one of them.
try
#property
def bar:
return super(FooBar, self).bar
Although I'm not sure if python supports calling the base class property. A property is actually a callable object which is set up with the function specified and then replaces that name in the class. This could easily mean that there is no super function available.
You could always switch your syntax to use the property() function though:
class Foo(object):
def _getbar(self):
return 5
def _setbar(self, a):
print a
bar = property(_getbar, _setbar)
class FooBar(Foo):
def _getbar(self):
# return the same value
# as in the base class
return super(FooBar, self)._getbar()
def bar(self, c):
super(FooBar, self)._setbar(c)
print "Something else"
bar = property(_getbar, _setbar)
fb = FooBar()
fb.bar = 7
Some small improvements to Maxime's answer:
Using __class__ to avoid writing B. Note that self.__class__ is the runtime type of self, but __class__ without self is the name of the enclosing class definition. super() is a shorthand for super(__class__, self).
Using __set__ instead of fset. The latter is specific to propertys, but the former applies to all property-like objects (descriptors).
class B(A):
#property
def prop(self):
value = super().prop
# do something with / modify value here
return value
#prop.setter
def prop(self, value):
# do something with / modify value here
super(__class__, self.__class__).prop.__set__(self, value)
You can use the following template:
class Parent():
def __init__(self, value):
self.__prop1 = value
#getter
#property
def prop1(self):
return self.__prop1
#setter
#prop1.setter
def prop1(self, value):
self.__prop1 = value
#deleter
#prop1.deleter
def prop1(self):
del self.__prop1
class Child(Parent):
#getter
#property
def prop1(self):
return super(Child, Child).prop1.__get__(self)
#setter
#prop1.setter
def prop1(self, value):
super(Child, Child).prop1.__set__(self, value)
#deleter
#prop1.deleter
def prop1(self):
super(Child, Child).prop1.__delete__(self)
Note! All of the property methods must be redefined together. If do not want to redefine all methods, use the following template instead:
class Parent():
def __init__(self, value):
self.__prop1 = value
#getter
#property
def prop1(self):
return self.__prop1
#setter
#prop1.setter
def prop1(self, value):
self.__prop1 = value
#deleter
#prop1.deleter
def prop1(self):
del self.__prop1
class Child(Parent):
#getter
#Parent.prop1.getter
def prop1(self):
return super(Child, Child).prop1.__get__(self)
#setter
#Parent.prop1.setter
def prop1(self, value):
super(Child, Child).prop1.__set__(self, value)
#deleter
#Parent.prop1.deleter
def prop1(self):
super(Child, Child).prop1.__delete__(self)
class Base(object):
def method(self):
print "Base method was called"
class Derived(Base):
def method(self):
super(Derived,self).method()
print "Derived method was called"
d = Derived()
d.method()
(that is unless I am missing something from your explanation)

Categories

Resources