Use default init values of other classes - python

I have 2 classes with some functions:
class A:
def __init__(self, one=1, two=2):
self.one = one
self.two = two
def do_smt(self):
...
class B:
def __init__(self, value="test"):
self.value = value
def do_smt(self):
...
I have a third class that has to use the 2 classes is doing this.
class C:
def __init__(self, one=1, two=2, value="test"):
self.A = A(one, two)
self.B = B(value)
def do_smt(self):
...
Now I do this: new_class = C()
But what if the default value of class A or B changes, then I also need to change that in class C. Is there a way to write class C in a way that it knows which arguments are the default ones? It need to handle no arguments but also arguments that other classes expect.

You can use inspect.signature to obtain the parameters of the __init__ method of each "base" class of class C, and let C.__init__ accept variable keyword arguments, so that it can iterate through the "base" classes and pass to the __init__ method of each just what it needs and what the given keyword arguments have. Use itertools.islice to ignore the first parameter, which is always self:
import inspect
from itertools import islice
class C:
bases = A, B
params = {}
for cls in bases:
params[cls] = inspect.signature(cls.__init__).parameters
def __init__(self, **kwargs):
for cls in self.bases:
setattr(self, cls.__name__, cls(**{key: kwargs[key] for key in
islice(self.params[cls], 1, None) if key in kwargs}))
so that:
c = C(one=3,value='hi')
print(c.A.one)
print(c.A.two)
print(c.B.value)
outputs:
3
2
hi

You could use some sentinel value (here None) and pass parameters only if they are provided as something meaningful:
class C:
def __init__(self, one=None, two=None, value=None):
if one is two is None:
self.A = A()
else:
self.A = A(one, two)
if value is None:
self.B = B()
else:
self.B = B(value)
That way, A and B's defaults take care of themselves.

One solution is to factor the default values to constants:
DEFAULT_ONE = 1
DEFAULT_TWO = 2
class A:
def __init__(self, one=DEFAULT_ONE, two=DEFAULT_TWO):
pass
Use the constants in class C as well.

Before the call class A and B, define init values to variables
Try add these before calls in class C init:
self.initA_one = A.one
self.initA_two = A.two
self.initB_value = B.value
And continue
self.A = A (.,.)
self.B = B (.)
EDIT:
this was what i meant.
class C():
def __init__(self, one=-1, two=-2, value="detest"):
self.initA_one = A().one
self.initA_two = A().two
self.initB = B().value
self.A = A(one, two)
self.B = B(value)
def do_smt(self):
print()
new_class = C()
print(f'default A.one is {new_class.initA_one}, new value A.one is {new_class.A.one}.')
print(f'default A.two is {new_class.initA_two}, new value A.two is {new_class.A.two}.')
print(f'default B.value is {new_class.initB}, new B.value is {new_class.B.value}')
gives
default A.one is 1, new value A.one is -1.
default A.two is 2, new value A.two is -2.
default B.value is test, new B.value is detest

I am not sure if this fits exactly what you want, but basically you can let C decide what to give to A, B and let A, B decide what to use, using **kwds method parameters in A and B.
One of the differences, with the sample class C2, is that, if C has a different default value it overrides A, B.
There is also another alternative, under C3, where you use a guard value (not using None to allow that to be a default) to only pass on arguments that were given to C3.
class A:
def __init__(self, one=1, two=2, **kwds):
self.one = one
self.two = two
def do_smt(self):
pass
class B:
def __init__(self, value="test", **kwds):
self.value = value
class C:
def __init__(self, one=1, two=2, value="test"):
self.A = A(one, two)
self.B = B(value)
class C2:
""" your default values override those of A, B"""
def __init__(self, one=1, two=2, value="test"):
locals_ = locals()
locals_.pop("self")
self.A = A(**locals_)
self.B = B(**locals_)
undefined = NotImplemented
class C3:
""" your default values dont affect A and Bs"""
def __init__(self, one=undefined, two=undefined, value="test"):
locals_ = {k:v for k,v in locals().items() if k != "self" and v is not undefined}
self.A = A(**locals_)
self.B = B(**locals_)
#can still use it locally
self.one = one if one is not undefined else 11
self.two = two if two is not undefined else 22
c= C()
print("c.A.one:", c.A.one)
print("c.B.value:", c.B.value)
c2= C2()
print("c2.A.one:", c2.A.one)
print("c2.B.value:", c2.B.value)
c3= C3()
print("c3.A.one:", c3.A.one)
print("c3.one:", c3.one)
print("c3.B.value:", c3.B.value)
output:
c.A.one: 1
c.B.value: test
c2.A.one: 1
c2.B.value: test
c3.A.one: 1
c3.one: 11
c3.B.value: test
You could even have a variant of C that uses **kwds itself and pass those on to A, B in case they find value in it.
class C4:
""" your default values dont affect A and Bs
and you can pass in anything.
Neither two or value are known to C and that's OK"""
def __init__(self, one=undefined, **kwds):
locals_ = locals()
locals_ = {k:v for k,v in locals().items() if k not in ("self","kwds") and v is not undefined}
locals_.update(**kwds)
self.A = A(**locals_)
self.B = B(**locals_)
#can still use it locally
self.one = one if one is not undefined else 11
c4= C4(value="somevalue")
print("c4.A.one:", c4.A.one)
print("c4.A.two:", c4.A.two)
print("c4.one:", c4.one)
print("c4.B.value:", c4.B.value)
output:
c4.A.one: 1
c4.A.two: 2
c4.one: 11
c4.B.value: somevalue

I think the best (and pythonic) way to write such classes is always using None as default for any optional argument in any class. In class A and B you then check whether the respective argument is None and if so, replace by your real default value. That way the default for each attribute is defined in only one place and class C doesn't need to know the default value.

Related

How to preserve the value of class properties

class A:
p = 1
def __init__(self, p=None, **kwargs):
self.p = p
class B(A):
p = 2
a = A()
print(a.p)
b = B()
print(b.p)
As a more sensible example consider:
class Mamal:
can_fly = False
class Bat(Mamal):
can_fly = True
In the examples above, I would like 1 and 2 be printed. However, it prints None for both, though I know why. What is the solution to preserve the default value of classes?
One solution I can think of is:
class A:
p = 1
def __init__(self, p=None, **kwargs):
if p: self.p = p
if q: self.q = q
...
and if I have many attributes I should do that for all of them!? another minor problem is that the user can't pass None to the class init.
Another solution could be like:
class A:
p = 1
def __init__(self, p=1, **kwargs):
self.p = p
self.q = q
...
However again if one instantiate b like:
b = B()
the value of b.p would be also 1, while I expect it to keep 2.
I use overriding classes attributes much, but I just don't know how to preserve them from being overwritten by default values of the same or parent class.
Yet, another solution is combination of the above, like:
class A:
p = 1
def __init__(self, p=1, **kwargs):
if p != 1: self.p = p
...
or using dataclass
from dataclasses import dataclass
#dataclass
class A:
p :int = 1
#dataclass
class B(A):
p:int = 2
Just would like to know what is usual approach and consequences.
UPDATE:
If you really absolutely need both your class and your instances to have this attribute, and also want to use the class attribute as the default for an instance, I would say the correct way is like this:
_sentinel = object()
class A:
p = 1
def __init__(self, p=_sentinel):
if p is not _sentinel:
self.p = p
class B(A):
p = 2
a = A()
print(a.p) # prints 1
b = B()
print(b.p) # prints 2
b2 = B(p=None)
print(b2.p) # prints None
The sentinel object is for when you do want to be able to pass None to the constructor for whatever reason. Since we compare identity in the __init__ method, it is (practically) guaranteed that if any value is passed, it will be assigned to the instance attribute, even if that value is None.
Original answer:
The problem seems to stem from a misunderstanding of how (class-)attribute work in Python.
When you do this:
class A:
p = 1
You define a class attribute. Instances of that class will automatically have that same attribute upon initialization, unless you overwrite it, which is exactly what you do here:
def __init__(self, p=None, **kwargs):
self.p = p
This overwrites the instance's attribute .p with the value p it receives in the __init__ method. In this case, since you defined a default value None and called the constructor without passing an argument, that is what was assigned to the instance's attribute.
If you want, you can simply omit the self.p assignment in the constructor. Then your instances will have the class' default upon initialization.
EDIT:
Depending on how you want to handle it, you can simply assign the value after initialization. But I doubt that is what you want. You probably don't need class attributes at all. Instead you may just want to define the default values in your __init__ method signature and assign them there.
If you really need that class attribute as well, you can do what you did, but more precisely by testing for if p is not None:.
I would set the default value of the p argument to the value that you want:
class A:
def __init__(self, p=1, **kwargs):
self.p = p
class B(A):
def __init__(self, p=2, **kwargs):
super().__init__(p, **kwargs)
a = A()
print(a.p)
b = B()
print(b.p)
Then from the constructor of B you can call the one from A by using super().__init__
You can use class properties from the class:
class A:
p = 1
class B(A):
p = 2
a = A()
print(a.p)
b = B()
print(b.p)
prints 1 and 2, like you wanted.
It is clearer to access them from the class directly, though:
print(A.p)
print(B.p)
You can set the instance one, without changing what is associated in the class.
class B(A):
def change(self, x):
self.p = x
b.change(3)
print(B.p) #2
print(b.p) #3

Can a python class method have as arguments the same class variable updated in init(self)?

I have two python classes
class A:
"""
This is a class retaining some constants
"""
C=1
class B:
VAR = None
def __init__(self):
b.VAR = A
def f(self, v=VAR ):
print(v.C)
clb = B()
clb .f()
AttributeError: 'NoneType' object has no attribute 'C'
So what I am trying to do is populate the B::VAR class variable in the B::init() with the reference of class A, and after that in the B::f() to have access to A::C by using default argument v (that retains VAR).
I intend to use v as a default value for the code inside B::f() and if needed to change it when calling the function.
Is my scenario possible?
Thank you,
Yes, this is possible:
class A:
"""
This is a class retaining some constants
"""
C = 1
class B:
VAR = None
def __init__(self):
self.VAR = A
def f(self, v=None):
if v is None:
v = self.VAR
print(v.C)
clb = B()
clb.f()
You issue is that the default arguments v=VAR is an old reference to the B.VAR which is None, not the updated value of the object clb.VAR.
This diagram show that the old version of f() have a default value for v that point to None, because this is computed at the definition of the method, when the class B is defined, before any creation of clb: B object, where VAR is a class attribute.
My suggestion is to set v at runtime using the VAR of the object throught self, which is changed in the __init__ to A.
class A:
C = 1
class B:
VAR = None
def __init__(self):
B.VAR = A
#classmethod
def f(cls):
print(cls.VAR.C)
clb = B()
clb.f()
This is another way to do it. However, I'm wondering what it is you're actually trying to do, because this seems really strange

conditional class inheritance in python

I am trying to dynamically create classes in Python and am relatively new to classes and class inheritance. Basically I want my final object to have different types of history depending on different needs. I have a solution but I feel there must be a better way. I dreamed up something like this.
class A:
def __init__(self):
self.history={}
def do_something():
pass
class B:
def __init__(self):
self.history=[]
def do_something_else():
pass
class C(A,B):
def __init__(self, a=False, b=False):
if a:
A.__init__(self)
elif b:
B.__init__(self)
use1 = C(a=True)
use2 = C(b=True)
You probably don't really need that, and this is probably an XY problem, but those happen regularly when you are learning a language. You should be aware that you typically don't need to build huge class hierarchies with Python like you do with some other languages. Python employs "duck typing" -- if a class has the method you want to use, just call it!
Also, by the time __init__ is called, the instance already exists. You can't (easily) change it out for a different instance at that time (though, really, anything is possible).
if you really want to be able to instantiate a class and receive what are essentially instances of completely different objects depending on what you passed to the constructor, the simple, straightforward thing to do is use a function that returns instances of different classes.
However, for completeness, you should know that classes can define a __new__ method, which gets called before __init__. This method can return an instance of the class, or an instance of a completely different class, or whatever the heck it wants. So, for example, you can do this:
class A(object):
def __init__(self):
self.history={}
def do_something(self):
print("Class A doing something", self.history)
class B(object):
def __init__(self):
self.history=[]
def do_something_else(self):
print("Class B doing something", self.history)
class C(object):
def __new__(cls, a=False, b=False):
if a:
return A()
elif b:
return B()
use1 = C(a=True)
use2 = C(b=True)
use3 = C()
use1.do_something()
use2.do_something_else()
print (use3 is None)
This works with either Python 2 or 3. With 3 it returns:
Class A doing something {}
Class B doing something []
True
I'm assuming that for some reason you can't change A and B, and you need the functionality of both.
Maybe what you need are two different classes:
class CAB(A, B):
'''uses A's __init__'''
class CBA(B, A):
'''uses B's __init__'''
use1 = CAB()
use2 = CBA()
The goal is to dynamically create a class.
I don't really recommend dynamically creating a class. You can use a function to do this, and you can easily do things like pickle the instances because they're available in the global namespace of the module:
def make_C(a=False, b=False):
if a:
return CAB()
elif b:
return CBA()
But if you insist on "dynamically creating the class"
def make_C(a=False, b=False):
if a:
return type('C', (A, B), {})()
elif b:
return type('C', (B, A), {})()
And usage either way is:
use1 = make_C(a=True)
use2 = make_C(b=True)
I was thinking about the very same thing and came up with a helper method for returning a class inheriting from the type provided as an argument.
The helper function defines and returns the class, which is inheriting from the type provided as an argument.
The solution presented itself when I was working on a named value class. I wanted a value, that could have its own name, but that could behave as a regular variable. The idea could be implemented mostly for debugging processes, I think. Here is the code:
def getValueClass(thetype):
"""Helper function for getting the `Value` class
Getting the named value class, based on `thetype`.
"""
# if thetype not in (int, float, complex): # if needed
# raise TypeError("The type is not numeric.")
class Value(thetype):
__text_signature__ = "(value, name: str = "")"
__doc__ = f"A named value of type `{thetype.__name__}`"
def __init__(self, value, name: str = ""):
"""Value(value, name) -- a named value"""
self._name = name
def __new__(cls, value, name: str = ""):
instance = super().__new__(cls, value)
return instance
def __repr__(self):
return f"{super().__repr__()}"
def __str__(self):
return f"{self._name} = {super().__str__()}"
return Value
Some examples:
IValue = getValueClass(int)
FValue = getValueClass(float)
CValue = getValueClass(complex)
iv = IValue(3, "iv")
print(f"{iv!r}")
print(iv)
print()
fv = FValue(4.5, "fv")
print(f"{fv!r}")
print(fv)
print()
cv = CValue(7 + 11j, "cv")
print(f"{cv!r}")
print(cv)
print()
print(f"{iv + fv + cv = }")
The output:
3
iv = 3
4.5
fv = 4.5
(7+11j)
cv = (7+11j)
iv + fv + cv = (14.5+11j)
When working in IDLE, the variables seem to behave as built-in types, except when printing:
>>> vi = IValue(4, "vi")
>>> vi
4
>>> print(vi)
vi = 4
>>> vf = FValue(3.5, 'vf')
>>> vf
3.5
>>> vf + vi
7.5
>>>

returning instances in python

class classname():
def func(self,a,b):
self.c = a+b
self.d = a-b
self.e = a*b
return self
cn = classname()
This way i can access cn.c, cn.d and cn.e can i use something else other then self to return it and it will be a structure. I know its possible in matlab where you can define structure in a function. Something what i expect should look like this:
class classname():
def func(self,newself,a,b):
self.c = a+b
self.d = a-b
newself.e = a*b
return self, newself
cn = classname()
I know this is not a valid code but just an idea what i want from code.
I think what you want is this:
class classname:
def __init__(self, a, b):
self.c = a+b
self.d = a-b
self.e = a*b
cn = classname(12, 34) # Just random values for 'a' and 'b'. Use whatever you like!
print(cn.c)
>>> 46
print(cn.d)
>>> -22
print(cn.e)
>>> 408
The __init__ function is automatically called when the object is created. Self will always refer to the object, so adding attributes to it will add it to the object, so you don't need to return anything.

Using classmethods to implement alternative constructors, how can I add functions to those alternative constructors?

I have an class that can be constructed via alternative constructors using class methods.
class A:
def __init__(self, a, b):
self.a = a
self.b = b
#classmethod
def empty(cls, b):
return cls( 0 , b)
So let's say instead of constructing A like A() I can now also do A.empty().
For user convenience, I would like to extend this empty method even further, so that I can initialize A via A.empty() as well as the more specialized but closely-related A.empty.typeI() and A.empty.typeII().
My naive approach did not quite do what I wanted:
class A:
def __init__(self, a, b):
self.a = a
self.b = b
#classmethod
def empty(cls, b):
def TypeI(b):
return cls( 0 , b-1)
def TypeII(b):
return cls( 0 , b-2)
return cls( 0 , b)
Can anyone tell me how that could be done (or at least convince me why that would be terrible idea). I want to stress that for usage I imagine such an approach to be very convenient and clear for the users as the functions are grouped intuitively.
You can implement what you want by making Empty a nested class of A rather than a class method. More than anything else this provides a convenient namespace — instances of it are never created — in which to place various alternative constructors and can easily be extended.
class A(object):
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return 'A({}, {})'.format(self.a, self.b)
class Empty(object): # nested class
def __new__(cls, b):
return A(0, b) # ignore cls & return instance of enclosing class
#staticmethod
def TypeI(b):
return A(0, b-1)
#staticmethod
def TypeII(b):
return A(0, b-2)
a = A(1, 1)
print('a: {}'.format(a)) # --> a: A(1, 1)
b = A.Empty(2)
print('b: {}'.format(b)) # --> b: A(0, 2)
bi = A.Empty.TypeI(4)
print('bi: {}'.format(bi)) # --> bi: A(0, 3)
bii = A.Empty.TypeII(6)
print('bii: {}'.format(bii)) # --> bii: A(0, 4)
You can’t really do that because A.empty.something would require the underlying method object to be bound to the type, so you can actually call it. And Python simply won’t do that because the type’s member is empty, not TypeI.
So what you would need to do is to have some object empty (for example a SimpleNamespace) in your type which returns bound classmethods. The problem is that we cannot yet access the type as we define it with the class structure. So we cannot access its members to set up such an object. Instead, we would have to do it afterwards:
class A:
def __init__ (self, a, b):
self.a = a
self.b = b
#classmethod
def _empty_a (cls, b):
return cls(1, b)
#classmethod
def _empty_b (cls, b):
return cls(2, b)
A.empty = SimpleNamespace(a = A._empty_a, b = A._empty_b)
Now, you can access that member’s items and get bound methods:
>>> A.empty.a
<bound method type._empty_a of <class '__main__.A'>>
>>> A.empty.a('foo').a
1
Of course, that isn’t really that pretty. Ideally, we want to set this up when we define the type. We could use meta classes for this but we can actually solve this easily using a class decorator. For example this one:
def delegateMember (name, members):
def classDecorator (cls):
mapping = { m: getattr(cls, '_' + m) for m in members }
setattr(cls, name, SimpleNamespace(**mapping))
return cls
return classDecorator
#delegateMember('empty', ['empty_a', 'empty_b'])
class A:
def __init__ (self, a, b):
self.a = a
self.b = b
#classmethod
def _empty_a (cls, b):
return cls(1, b)
#classmethod
def _empty_b (cls, b):
return cls(2, b)
And magically, it works:
>>> A.empty.empty_a
<bound method type._empty_a of <class '__main__.A'>>
Now that we got it working somehow, of course we should discuss whether this is actually something you want to do. My opinion is that you shouldn’t. You can already see from the effort it took that this isn’t something that’s usually done in Python. And that’s already a good sign that you shouldn’t do it. Explicit is better than implicit, so it’s probably a better idea to just expect your users to type the full name of the class method. My example above was of course structured in a way that A.empty.empty_a would have been longer than just a A.empty_a. But even with your name, there isn’t a reason why it couldn’t be just an underscore instead of a dot.
And also, you can simply add multiple default paths inside a single method. Provide default argument values, or use sensible fallbacks, and you probably don’t need many class methods to create alternative versions of your type.
It is generally better to have uniform class interfaces, meaning the different usages should be consistent with each other. I consider A.empty() and A.empty.type1() to be inconsistent with each other, because the prefix A.empty ituitively means different things in each of them.
A better interface would be:
class A:
#classmethod
def empty_default(cls, ...): ...
#classmethod
def empty_type1(cls, ...): ...
#classmethod
def empty_type2(cls, ...): ...
Or:
class A:
#classmethod
def empty(cls, empty_type, ...): ...
Here's an enhanced implementation of my other answer that makes it — as one commenter put it — "play well with inheritance". You may not need this, but others doing something similar might.
It accomplishes this by using a metaclass to dynamically create and add an nested Empty class similar to that shown in the other answer. The main difference is that the default Empty class in derived classes will now return Derived instances instead of instances of A, the base class.
Derived classes can override this default behavior by defining their own nested Empty class (it can even be derived from the one in the one in the base class). Also note that for Python 3, metaclasses are specified using different syntax:
class A(object, metaclass=MyMetaClass):
Here's the revised implementation using Python 2 metaclass syntax:
class MyMetaClass(type):
def __new__(metaclass, name, bases, classdict):
# create the class normally
MyClass = super(MyMetaClass, metaclass).__new__(metaclass, name, bases,
classdict)
# add a default nested Empty class if one wasn't defined
if 'Empty' not in classdict:
class Empty(object):
def __new__(cls, b):
return MyClass(0, b)
#staticmethod
def TypeI(b):
return MyClass(0, b-1)
#staticmethod
def TypeII(b):
return MyClass(0, b-2)
setattr(MyClass, 'Empty', Empty)
return MyClass
class A(object):
__metaclass__ = MyMetaClass
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return '{}({}, {})'.format(self.__class__.__name__, self.a, self.b)
a = A(1, 1)
print('a: {}'.format(a)) # --> a: A(1, 1)
b = A.Empty(2)
print('b: {}'.format(b)) # --> b: A(0, 2)
bi = A.Empty.TypeI(4)
print('bi: {}'.format(bi)) # --> bi: A(0, 3)
bii = A.Empty.TypeII(6)
print('bii: {}'.format(bii)) # --> bii: A(0, 4)
With the above, you can now do something like this:
class Derived(A):
pass # inherits everything, except it will get a custom Empty
d = Derived(1, 2)
print('d: {}'.format(d)) # --> d: Derived(1, 2)
e = Derived.Empty(3)
print('e: {}'.format(e)) # --> e: Derived(0, 3)
ei = Derived.Empty.TypeI(5)
print('ei: {}'.format(ei)) # --> ei: Derived(0, 4)
eii = Derived.Empty.TypeII(7)
print('eii: {}'.format(eii)) # --> eii: Derived(0, 5)

Categories

Resources