I'm trying to find an elegant pythonic solution for the following design issue:
Within a class I want to use properties as default parameter.
So, one could do something like:
Class A:
def __init__(self, a=0, b=1):
self.a = a
self.b = b
def func(self, a=None, b=None):
a = a or self.a
b = b or self.b
# do something with a and b
But since this seems like potential boilerplate code, I went ahead and wrote a generic function outside of my class.
def fallback_parameters(obj, **args):
return [args[arg] or getattr(obj, arg) for arg in args]
and use
def func(self, a=None, b=None):
a, b = fallback_parameters(self, a=a, b=b)
# do something with a and b
Is there a better/cleaner way to achieve this? An existing solution?
I'm tempted to create a decorator, so I don't need to type something like:
a, b, c, d, e = fallback_parameters(self, a=a, b=b, c=c, d=d, e=e)
Just for illustration if you wanted it to change the object instance attibute (you say you do not) the decorator would be like this:
def fallback_args(func):
def inner(obj, **kwargs):
if kwargs:
for key in kwargs:
setattr(obj, key, kwargs[key])
return func(obj, **kwargs)
return inner
class B:
def __init__(self, a=0, b=1):
self.a = a
self.b = b
#fallback_args
def func(self, a=None, b=None):
print("Do something with {} and {}".format(self.a, self.b))
#or
#a = self.a
#b = self.b
#print("Do something with {} and {}".format(a, b))
obj2 = B()
print(obj2.a)
print(obj2.b)
obj2.func()
print()
obj2 = B()
print(obj2.a)
print(obj2.b)
obj2.func(a=300)
print()
print(obj2.a)
print(obj2.b)
Outputs:
0
1
Do something with 0 and 1
0
1
Do something with 300 and 1
300
1
But not wanting to change the object, I don't think it makes sense just to avoid
explicit a = self.a or a like lines. Because if you don't list the parameters in the call, how would the decorator know what names do you mention in the methods' body to fallback/load them? You can say, the names are only the ones listed in the method signature. Now, that can be introspected, but I think I am seeing more convoluted work here than this deserves. a = self.a or a looks simple and explicit. So it is Pythonic too, although you probably could write the implicit assignment you want with some more work.
For an idea on how to introspect method args see a somewhat related decorator I wrote here to freeze methods args.
Related
I have the following situation:
class Foo:
def __init__(self, O):
self.a = O.some_attr.calc_a()
self.b = O.some_other_attr.calc_b()
Note that O cannot be reconstructed from a and b. Now, I also want to be able to initialise Foo directly by passing a and b, but I only want to do this internally, the standard way should be by passing O.
I know I can do something like
class Foo:
def __init__(self, a, b):
self.a = a
self.b = b
#classmethod
def from_O(cls, O):
return cls(O.some_attr.cal_a(), O.some_other_attr.cal_b())
but this has the disadvantage that the standard call now becomes the more cumbersome Foo.from_O(O).
In other words, how can I achieve Foo(O) and Foo.from_a_b(a,b) when O is not reconstructable from a and b?
Can I have a classmethod that avoids calling __init__?
(Note: I am looking for a 'clean' way to do this. I know I can dissect the argument list or do something like
class _Foo:
def __init__(self, a, b):
self.a = a
self.b = b
class Foo(_Foo):
def __init__(self, O):
super().__init__(O.some_attr.cal_a(), O.some_other_attr.cal_b())
but this seems a rather awkward solution.)
You could make O, a, and b all optional arguments to the same __init__ method and make a distinction on whether O is given or not.
class Foo:
def __init__(self, O=None, a=None, b=None):
if O is not None:
self.a = O.some_attr.calc_a()
self.b = O.some_other_attr.calc_b()
# ignore a and b
else:
if a is None or b is None:
raise TypeError("If O is not given, a and b cannot be None")
self.a = a
self.b = b
Usage:
# from O
foo_from_O = Foo(O)
# from a, b
foo_from_a_b_1 = Foo(None, 'a', 'b')
foo_from_a_b_2 = Foo(a='a', b='b')
I tried to find out with googling, but it seems not explained.
There are four classes, for example, which are A, B, C, and D.
C is sub-class of D. A and B is calling C.
Class A:
def __init__(self):
self.c = C()
Class B:
def __init__(self):
self.c = C()
Class C(D):
def __init__(self):
print('it is C')
Class D:
def __init__(self):
print('it is D')
a = A()
b = b()
In this case, it will initiate twice for Class C. So, 'it is C' and 'it is D' strings represent twice. And.. I think it's ... not normal.
Unlike Java, it seems that Python doesn't support constructor overloading.
In this case, can I call C only though? Or, could I get an advice for using init in Python?
Thank you.
You have several errors in your code: class not Class, checkout indentation, class definition order... see this:
class D:
def __init__(self):
print('it is D')
class C(D):
def __init__(self):
print('it is C')
class A:
def __init__(self):
self.c = C()
class B:
def __init__(self):
self.c = C()
a = A()
b = B()
method 1
you can use the *args and **kwargs
args and kwargs
you can get the len(args) and len(kwargs.keys()) (kwargs is a dict) and program different parts depending on the number of arguments. this can work like a constructor overloading
example:
__init__(self, *args):
if len(args)==0:
# constructor one
if len(args)==1:
# constructor two
method 2
use default arguments, most likely argname=None. then use if-else with the value of that argument
simple example :
__init__(self, arg1=None):
if arg1==None:
# constructor one
else:
# constructor two
Let's say I have a class called Adder:
class adder(object):
def __init__(self, a, b):
self.a=a
self.b=b
self.result = None
def perform_addition(self):
self.result = self.a + self.b
return self.result
If I instantiate this class:
myAdder = adder(1,2)
Then the value of myAdder.result depends on calling perform_addition() first, otherwise it'll always remain None. It other words, there's a dependency on perform_addition() for the value of self.result. And if we extrapolate, a more complex class can have a chain of dependencies: ie, you have to call functions A, B, and C before D, because they in turn populate the necessary variables that the next function needs.
Is this bad class design? What is the remedy for it?
I think the above is a example of: https://en.wikipedia.org/wiki/Sequential_coupling
I think it all depends on what you want to do and how you want to go about it. the code you have is not necessarily bad, if you want a static dependency on 'perform_addition()' for the value of 'self.result' . But if you want a dynamic dependency, then the code below will be a good and simple approach. this way when an object is created by instantiating the class with values 'a' and 'b', 'self.result' will be automatically computed. you could also use more advanced tools like properties, decorators, descriptors etc. like i said, it all depends on what you want.
Class adder(object):
def __init__(self, a, b):
self.a=a
self.b=b
self.result = self.perform_addition()
def perform_addition(self):
self.result = self.a + self.b
return self.result
This would be a good case to make result a property instead, so that the addition is only performed when the result attribute is accessed:
class adder(object):
def __init__(self, a, b):
self.a = a
self.b = b
#property
def result(self):
return self.a + self.b
myAdder = adder(1,2)
print(myAdder.result)
This outputs: 3
In case the result attribute is expected to be accessed multiple times and that the calculation involved is expensive, you can save the result in an instance variable to avoid re-calculations:
class adder(object):
def __init__(self, a, b):
self.a = a
self.b = b
self._result = None
#property
def result(self):
if self._result is None:
self._result = self.a + self.b
return self._result
I came across to a piece of Python Code which technically looks like this:
def bar(b):
return b*2
class MyClass(object):
def __init__(self, a):
self.a = a
self.b = 10
def foo(self, b=None):
return bar(b if b is not None else self.b)
So the class has b as a fixed attribute, which is used as default input for a classmethod foo, if it is called without argument.
Please assume that b in this case is expected to be a float.
Please ignore unused self.a, it is used otherwise - i just wanted to make sure that you see that there are attributes settable by the constructor.
The usage would look like this:
c = MyClass(2)
c.foo(3) # returns 3*2 = 6
c.foo() # returns 10*2 = 20
In this case, I was wondering about the foo-method. Is this a good way to implement self.b as default value for b?
Is one of the following two suggestions by me more or less desireable? If so: why?
def foo(self, b=None):
return bar(b or self.b)
or
def foo(self, b=self.b):
return bar(b)
Stick with the first option:
def foo(self, b=None):
return bar(b if b is not None else self.b)
If you use the second option:
def foo(self, b=None):
return bar(b or self.b)
then you will alter the behaviour of x.foo(0), because zero is also falsey.
The third option is not an option:
def foo(self, b=self.b):
return bar(b)
self does not exist when the method foo() is defined, so you cannot use self.b as a default argument. The definition will simply fail; or worse, it will try and use whatever is the current value of self when the definition is executed.
I have a class:
class A(object):
def __init__(self,a,b,c,d,e,f,g,...........,x,y,z)
#do some init stuff
And I have a subclass which needs one extra arg (the last W)
class B(A):
def __init__(self.a,b,c,d,e,f,g,...........,x,y,z,W)
A.__init__(self,a,b,c,d,e,f,g,...........,x,y,z)
self.__W=W
It seems dumb to write all this boiler-plate code, e.g passing all the args from B's Ctor to the inside call to A's ctor, since then every change to A's ctor must be applied to two other places in B's code.
I am guessing python has some idiom to handle such cases which I am unaware of. Can you point me in the right direction?
My best hunch, is to have a sort of Copy-Ctor for A and then change B's code into
class B(A):
def __init__(self,instanceOfA,W):
A.__copy_ctor__(self,instanceOfA)
self.__W=W
This would suit my needs since I always create the subclass when given an instance of the father class, Though I am not sure whether it's possible...
Considering that arguments could be passed either by name or by position, I'd code:
class B(A):
def __init__(self, *a, **k):
if 'W' in k:
w = k.pop('W')
else:
w = a.pop()
A.__init__(self, *a, **k)
self._W = w
Edit: based on Matt's suggestion, and to address gnibbler's concern re a positional-argument approach; you might check to make sure that the additional subclass-specific argument is being specified—similar to Alex's answer:
class B(A):
def __init__(self, *args, **kwargs):
try:
self._w = kwargs.pop('w')
except KeyError:
pass
super(B,self).__init__(*args, **kwargs)
>>> b = B(1,2,w=3)
>>> b.a
1
>>> b.b
2
>>> b._w
3
Original answer:
Same idea as Matt's answer, using super() instead.
Use super() to call superclass's __init__() method, then continue initialising the subclass:
class A(object):
def __init__(self, a, b):
self.a = a
self.b = b
class B(A):
def __init__(self, w, *args):
super(B,self).__init__(*args)
self.w = w
In situations where some or all of the arguments passed to __init__ have default values, it can be useful to avoid repeating the __init__ method signature in subclasses.
In these cases, __init__ can pass any extra arguments to another method, which subclasses can override:
class A(object):
def __init__(self, a=1, b=2, c=3, d=4, *args, **kwargs):
self.a = a
self.b = b
# …
self._init_extra(*args, **kwargs)
def _init_extra(self):
"""
Subclasses can override this method to support extra
__init__ arguments.
"""
pass
class B(A):
def _init_extra(self, w):
self.w = w
Are you wanting something like this?
class A(object):
def __init__(self, a, b, c, d, e, f, g):
# do stuff
print a, d, g
class B(A):
def __init__(self, *args):
args = list(args)
self.__W = args.pop()
A.__init__(self, *args)