I have a number of Python types that describe a hierarchy in the sense that they are increasingly specific in terms of their properties. Instead of trying to describe it in words, here is an example:
class A:
#property
def prop1(self):
return self._prop1
class B:
#property
def prop1(self):
return self._prop1
#property
def prop2(self):
return self._prop2
class C:
#property
def prop1(self):
return self._prop1
#property
def prop2(self):
return self._prop2
#property
def prop3(self):
return self._prop3
So, as you go down the class list, B has all of the properties of A and then some extra ones. C has all of the properties of B and then some extra ones, and so on. I would like to minimize the duplication of the above definitions if possible.
One obvious solution would be to use inheritance, making B a subclass of A and so on. However, the semantics of these types do not follow an is-a relationship; I do not want isinstance(bObject, A) to be True. Is there an alternative way in Python to straightforwardly allow this sharing of attributes without using subclasses?
You can use a decorator:
def has_prop1(cls):
#property
def prop1(self):
return self._prop1
cls.prop1 = prop1
return cls
#has_prop1
class A(object):
pass
Compositing would go like this:
#has_prop1
#has_prop2
class B(object):
pass
Or even like this:
def has_many_properties(cls):
return has_prop1(has_prop2(has_prop3(cls)))
#has_many_properties
class C(object):
pass
You say "I don't want to lie and say that C is an A when it really isn't. The characteristics of the types don't fit the semantics of subclassing." With your obscured example, it's not clear which way to go.
If you're saying this because there's additional functionality in class A that doesn't apply to class C, then I would hoist out the property part of the class compositionally, so you have A, B, C containing AProps, BProps, and CProps respectively, and give the property classes a proper inheritance relationship.
If there's no such additional functionality, and it's not just coincidental that the properties are built up the way you show, then mentally rename class A to class ProvidesProp1, note that class C is-a class that provides prop1, and accept that class C therefore is-a class A.
Related
I'm currently working on redesigning a class to be under an abstract base class. The current class has a method func that does some logic for two things, say A and B.
(note that all the code below is very simplified. There's a lot more functionality than what is shown)
class current_class:
def func(self):
# does stuff for A
# does stuff for B
During logic A, it loads a large dataset into a dictionary, say, dataset and later dataset.keys() is used for logic B, but other than that, A and B are independent of each other.
I will create an alternate class, say, another_class that is similar to current_class, but this class doesn't need B and only needs A. So something like
class another_class:
def func(self):
# does stuff for A
And then both will be under an abstract base class base. Since both inherited classes involves A, I plan on just creating a method in base class that does A, say, func_A. But I'm having trouble with figuring out the best way to approach this so that the function signatures conform and without having to reload dataset for B.
If another_class also needed the logic for B, I think we can just return dataset.keys() from func_A and use it in func_B, but another_class doesn't.
So I don't know if there's a good way to conform this without having different signatures for the methods.
So in code, I have the following two ideas:
1)
class base:
#abstractmethod
def func(self):
pass
def func_A(self):
# does stuff for A and gets the dataset
return dataset.keys()
class current_class:
def func_B(self, keys):
# does stuff for B
def func(self):
keys = self.func_A
self.func_B(keys)
class current_class:
def func(self):
_ = self.func_A() # the return is unused...
class base:
#abstractmethod
def func(self):
pass
class current_class:
def func_A(self):
# does stuff for A and gets the dataset
return dataset.keys()
def func_B(self, keys):
# does stuff for B
def func(self):
keys = self.func_A()
self.func_B(keys)
class current_class:
def func_A(self):
# does same stuff as func_A for current_class, and doesn't return anything
def func(self):
self.func_A()
I don't like the first design because func_A only needs to return something for one of the subclasses and not for all of them. I also don't like the second design because we have to separately implement func_A in each inherited class even though they're identical methods, except one needs to return something and the other doesn't.
It's not a big deal to ignore the return value of a function that is primarily called for its side effects. Just define func_A once in the base class and let both child classes use it as appropriate to their needs.
class Base:
#abstractmethod
def func(self):
pass
def func_A(self):
# does stuff for A and gets the dataset
return dataset.keys()
class Child1:
def func_B(self, keys):
# does stuff for B
def func(self):
keys = self.func_A
self.func_B(keys)
class Child2:
def func(self):
self.func_A()
If there is more in func_A that isn't necessary for Child2, then it should of course be split up to avoid doing unnecessary work in Child2.func. But simply returning a value is not in anyway time- or space-intensive, and should not be a concern.
This question is specific for python 3. Suppose I have a class hierarchy like this
class Base():
def calculate():
return 0
class Derived1(Base):
def calculate():
# some calculation
class Derived2(Base):
def calculate():
# some calculation
Now, what I want to do is make a class that defines a generic way to inherit from the Derived classes, and then overrides calculate. In other words, something in the spirit of C++ templates, to avoid copying over the subclasses code, but specify a generic way of subclassing, and then be able to define the subclasses as one liners, like shown below:
# pseudocode
class GenericDerived5(someGenericBase):
def calculate():
return super().calculate() + 5
class GenericDerived6(someGenericBase):
def calculate():
return super().calculate() + 5
class Derived5_1 = GenericDerived5(Derived1)
class Derived6_1 = GenericDerived6(Derived2)
(the calculation is not literally like this, just illustrating the combinatorial nature of the inheritance structure)
How would this code look like, and what are the relevant tools from python3 that I need? I've heard of metaclasses, but not very familiar.
class definition inside a factory-function body
The most straightforward way to go there is really straightforward - but can feel a bit awkward:
def derived_5_factory(Base):
class GenericDerived5(Base):
def calculate(self):
return super().calculate() + 5
return GenericDerived5
def derived_6_factory(Base):
class GenericDerived6(Base):
def calculate(self):
return super().calculate() + 6
return GenericDerived6
Derived5_1 = derived_5_factory(Derived1)
Derived6_2 = derived_6_factory(Derived2)
The inconvenient part is that your classes that need generic bases
have to be defined inside function bodies. That way, Python re-executes
the class statement itself, with a different Base, taking advantage
that in Python classes are first class objects.
This code have the inconveniences that (1) the class bodies must be inside functions, and (2) it can be the wrong approach at all:
Multiple inheritance
If you can have an extra inheritance level - that is the only difference for your example, this is the "correct" way to go. Actually, apart from having the former "GenericDerived" classes explicitly in their inheritance chain, they will behave exactly as intended:
class Base():
def calculate():
return 0
class Derived1(Base):
def calculate(self):
return 1
class Derived2(Base):
def calculate(self):
return 2
# mix-in bases:
class MixinDerived5(Base):
def calculate(self):
return super().calculate() + 5
class MixinDerived6(Base):
def calculate(self):
return super().calculate() + 6
Derived5_1 = type("Derived5_1", (MixinDerived5, Derived1), {})
Derived6_2 = type("Derived6_2", (MixinDerived6, Derived2), {})
Here, instead of using the class statement, a dynamic class is created with the type call, using both the class that needs a dybamic base and that dynamic base as its bases parameter. That is it - Derived5_1 is a fully working Python class with both Bases in its inheritance chain
Note that Python's super() will do exactly what common sense would expect it to do, "rerouting" itself through the extra intermediary "derived" classes before reaching "Base". So, this is what I get on the interactive console after pasting the code above:
In [6]: Derived5_1().calculate()
Out[6]: 6
In [7]: Derived6_2().calculate()
Out[7]: 8
A mix-in class, roughly speaking, is a class that isn't intended to be instantiated directly or act as a standalone base class (other than for other, more specialized mix-in classes), but to provide a small subset of functionality that another class can inherit.
In this case, your GenericDerived classes are perfect examples of mix-ins: you aren't creating instances of GenericDerived, but you can inherit from them to add a calculate method to your own class.
class Calculator:
def calculate(self):
return 9
class Calculator1(Calculator):
def calculate(self):
return super().calculate() + 5
class Calculator2(Calculator):
def calculate(self):
return super().calculate() + 10
class Base(Calculator):
...
Note that the Base and Calculator hierarchies are independent of each other. Base provides, in addition to whatever else it does, basic calculate functionality. A subclass of Base can use calculate that it inherits from Base (via Calculator), or it can inherit from a subclass of Calculator as well.
class Derived1(Base):
...
class Derived2(Base, Calculator1):
...
class Derived3(Base, Calculator2):
...
The detail question is I have a plenty of classes, say A, B, C, D...Z, they're all derived from 'Base'. They all have a method set_value. Now I need to have some subclasses to override set_value of A...Z, the implement of new set_value are the same for all. Theoretically, I can do something like class AA(A), class BB(B)... but it's tedious and not compact, I am not sure if one or all of A...Z need a subclass, I only want to create it when I create an object of the subclass.
In C++, I can do this easily via template:
template<class T>
class CCustom : public T
{
};
CCustom<vector<int> > obj1;
CCustom<list<char> > obj2;
Add some demo Python scripts here to help explain my question:
class Base:
def set_value(self):
pass
class A(Base):
def set_value(self):
print('A.set_value')
class B(Base):
def set_value(self):
print('B.set_value')
class C(Base):
def set_value(self):
print('C.set_value')
def more_set_value():
print('all subclasses do this')
class AA(A):
def set_value(self):
more_set_value()
super().set_value()
class BB(B):
def set_value(self):
more_set_value()
super().set_value()
class CC(C):
def set_value(self):
more_set_value()
super().set_value()
a = AA()
b = BB()
c = CC()
a.set_value()
b.set_value()
c.set_value()
You can see AA, BB and CC are almost same. It's boring when there are hundreds this kind of class need to put in my project. I reckon there must be a way to write a factory function, to create AA, BB and CC dynamically, so that I can do following:
AA = create_custom_subclass(A)
a = AA()
a.set_value()
Classes are first-class citizens in Python, i.e. you can treat them like any other object. For example you can do a simple factory:
def create_custom_subclass(cls):
class sub(cls):
def set_value(self):
more_set_value()
super().set_value()
return sub
AA = create_custom_subclass(A)
a = AA()
a.set_value()
Or you can do a mixin (uses less memory then a factory):
class Mixin:
def set_value(self):
more_set_value()
super().set_value()
class AA(Mixin, A):
pass
It's not entirely clear to me what it is you want to do, but I'll try to give you some pointers so to speak.
Unlinke C++, Python uses a dynamic type system, i.e. a variable can be assigned an object of any type. This gives you a whole range of possibilities.
class CustomClass(object):
def __init__(self, obj_of_t_class):
self.obj_of_t_class = obj_of_t_class
def set_value(self, value):
self.obj_of_t_class.some_method(value)
# do something else here
As long as obj_of_t_class has the methods you try to call, Python doesn't care if it's of type A, B or Z.
This should be roughly equivalent to what you want to do with the C++ template class.
I have a class parent and two subclasses child1(parent) and child2(parent) sort of like the following near code.
(edited to more properly show that the parent class is doing something)
class parent(object):
name = None
def __init__(self,e):
# process the common attributes
name = e.attrib['name']
def __new__(cls,e):
if e.attrib['type'] == 'c1':
return child1(e)
elif e.attrib['type'] == 'c2':
return child2(e)
else:
raise
class child1(parent):
extra1 = None
def __init__(self,e):
super(e)
# set attributes from e that are specific to type c1
class child2(parent):
extra2 = None
def __init__(self,e):
super(e)
# set attributes from e that are specific to type c2
The goal is to be able to get the "right" class based on the value of the parameter. So if I can say obj = parent(element) and obj will be either child1 or child2 depending on what the value of element.attrib['type'] is.
The problem is that inside parent.__new__, you're calling child1(e), while calls child1.__new__, which finds the implementation in parent.__new__ and calls it with the same e, which calls child1(e), which… so you get infinite recursion.
There are better ways to design this, but if you just want to fix your design, there are three options:
If you define __new__ in all of your subclasses, it won't fall through the parent.__new__. You can do this in a single step by interposing an intermediate class between parent and childN, so you only need intermediate.__new__. Or use a mixin that they all inherit, or …
Get rid of the inheritance. Is there really any reason child1 is-a parent here?
You seem to be looking for what in Smalltalk/ObjC terms is called a "class cluster", and you don't need the "visible face" of the cluster to be the base class in Python any more than you do in those languages.
For example:
class base(object):
pass
class parent(base):
def __new__(cls, e):
# same as before
class child1(base):
# etc.
In Python, you can even make parent an ABC, and register each childN with it so you can use isinstance and friends with it.
Finally, you can just catch the recursion by only handling __new__ on parent, not its subclasses:
def __new__(cls, e):
if cls is not parent:
return super(parent, cls).__new__(cls)
This is a lot easier if you don't have parent be a class at all, but just a normal function.
Using the base class is a pattern from languages where that's the only real way to do this. It's not necessary or helpful in Python.
Class Bar inherits from Foo:
class Foo(object):
def foo_meth_1(self):
return 'foometh1'
def foo_meth_2(self):
return 'foometh2'
class Bar(Foo):
def bar_meth(self):
return 'bar_meth'
Is there a way of turning all methods inherited from Foo private?
class Bar(Foo):
def bar_meth(self):
return 'bar_meth'
def __foo_meth_1(self):
return 'foometh1'
def __foo_meth_2(self):
return 'foometh2'
Python doesn't have privates, only obfuscated method names. But I suppose you could iterate over the methods of the superclass when creating the instance, removing them from yourself and creating new obfuscatingly named method names for those functions. setattr and getattr could be useful if you use a function to create obfuscated names.
With that said, it's a pretty cthuhlu-oid thing to do. You mention the intent is to keep the namespace cleaner, but this is more like mixing ammonia and chlorine. If the method needs to be hidden, hide it in the superclass. The don't create instances of the superclass -- instead create a specific class that wraps the hidden methods in public ones, which you could name the same thing but strip the leading whitespace.
Assuming I understand your intent correctly, I would suggest doing something like this:
class BaseFoo(object):
def __init__(self):
raise NotImplementedError('No instances of BaseFoo please.')
def _foo(self):
return 'Foo.'
def _bar(self):
return 'Bar.'
class HiddenFoo(BaseFoo):
def __init__(self): pass
class PublicFoo(BaseFoo):
def __init__(self): pass
foo = BaseFoo._foo
bar = BaseFoo._bar
def try_foobar(instance):
print 'Trying ' + instance.__class__.__name__
try:
print 'foo: ' + instance.foo
print 'bar: ' + instance.bar
except AttributeError, e:
print e
foo_1 = HiddenFoo()
foo_2 = PublicFoo()
try_foobar(foo_1)
try_foobar(foo_2)
And if PublicFoo.foo would do something more than BaseFoo.foo, you would write a wrapper that does whatever is needed, and then calls foo from the superclass.
This is only possible with Pyhtons's metaclasses. But this is quite sophisticated and I am not sure if it is worth the effort. For details have a look here
Why would you like to do so?
Since foo() and __foo() are completely different methods with no link between them, Python is unable to understand what you want to do. So you have to explain to it step by step, meaning (like sapth said) to remove the old methods and add new ones.
This is an Object Oriented Design flaw and a better approach would be through delegation:
class Basic:
def meth_1(self):
return 'meth1'
def meth_2(self):
return 'meth2'
class Foo(Basic):
# Nothing to do here
pass
class Bar:
def __init__(self):
self.dg = Basic()
def bar_meth(self):
return 'bar_meth ' + self.__meth_1()
def __meth_1(self):
return self.dg.meth_1()
def __meth_2(self):
return self.dg.meth_2()
While Foo inherits the Basic class because he wants the public methods from him, Bar will only delegate the job to Basic because he doesn't want to integrate Basic's interface into its own interface.
You can use metaclasses, but Boo will no longer be an actual subclass of Foo, unless you want Foo's methods to be both 'private' and 'public' in instances of Bar (you cannot selectively inherit names or delattr members inherited from parent classes). Here is a very contrived example:
from inspect import getmembers, isfunction
class TurnPrivateMetaclass(type):
def __new__(cls, name, bases, d):
private = {'__%s' % i:j for i,j in getmembers(bases[0]) if isfunction(j)}
d.update(private)
return type.__new__(cls, name, (), d)
class Foo:
def foo_meth_1(self): return 'foometh1'
def foo_meth_2(self): return 'foometh2'
class Bar(Foo, metaclass=TurnPrivateMetaclass):
def bar_meth(self): return 'bar_meth'
b = Bar()
assert b.__foo_meth_1() == 'foometh1'
assert b.__foo_meth_2() == 'foometh2'
assert b.bar_meth() == 'bar_meth
If you wanted to get attribute access working, you could create a new Foo base class in __new__ with all renamed methods removed.