Clean pythonic composition with default parameters - python

Suppose I want to compose two objects and I'd like to be able to be able to define multiple constructors that respect the default arguments.
class A:
def __init__(x,y=0):
self.x = x
self.y = y
class B:
def __init__(w,objA, z=1):
self.w = w
self.objA = objA
self.z = z
I'd like to have multiple constructors for B which allow me to either pass an object A or pass parameters and then construct A. Looking at What is a clean, pythonic way to have multiple constructors in Python? I can do:
class B:
def __init__(w, objA, z=1):
self.w = w
self.objA = objA
self.z=z
#classmethod
def from_values(cls,w,z,x,y):
objA = A(x,y)
return cls(w,objA,z)
The problem is that the default values are being overwritten and I'd like to keep them. The following of course does not work.
#classmethod
def from_values(cls,w,x,**kwargs):
objA = A(x,**kwargs)
return cls(w,objA,**kwargs)
So it seems I'm stuck with remembering the default values and calling them like this
#classmethod
def from_values(cls,w,x,z=1,y=0):
objA = A(x,y)
return cls(w,objA,z)
This is not what I want since I'd rather have the objects themselves handle the default values and not be forced remember the default values. I could do one better than the above and use:
#classmethod
def from_values(cls,w,x,z=1,**kwargs):
objA = A(x,**kwargs)
return cls(w,objA,z)
But in this case I still need to "remember" the default value for z. Is there a Pythonic solution to this? Is this a design problem? Can someone point me to a good design pattern or best practices? This problem compounds when composing with several objects...
class L:
def __init__(objM,objN):
self.objM = objM
self.objN = objN
#classmethod
def from_values(cls, m1,m2,m3,n1,n2):
objM = M(m1,m2,m3)
objN = N(n1,n2)
return cls(objM, objN)

First I would argue that this isn't what you want to do. As this scales up, trying to call your constructor will be tedious and error-prone.
You can solve both of our problems with an explicit dictionary.
class A:
def __init__(self, config):
self.x = config.get('x')
assert self.x # if you need it
self.y = config.get('y', 0)
class B:
def __init__(self, b_config, objA):
self.w = b_config.get('w')
self.objA = objA
self.z = b_config.get('z', 1)
#classmethod
def from_values(cls,b_config,a_config):
return cls(b_config, A(a_config))
B.from_values({'w':1, 'z':2}, {'x': 3, 'y': 4})
It's probably not as clever or neat as what you're looking for, but it does let you construct from an A if you already have it, or to pass in a configurable set of parameters in a more structured way.

Related

Python class __call__ method and dot notation

My Goal is to use dot notation to select strings from dictionarys using the SimpleNamespace modeule while having the ability to change which dictionary to use.
To do this i have tried modifying the class __call__ method to change the output based on a previously set variable. However, due to the use of the __call__ method it requires the use of () to be included which breaks the simple formatting of dot notation. Additinally i need to be able to use class methods as well to change the option i am looking for.
class i: x, y = 1, 2
class j: x, y = 3, 4
class myClass:
def __init__(self):
self.a, self.b = i(), j()
self.selection = "a"
def set_selection(self, selection):
self.selection = selection
def __call__(self):
return getattr(self, self.selection)
mc = myClass()
print(mc().x) ## this generates the output i am wanting by using the __call__ method
mc.set_selection("b") ## i still need to call class methods
print(mc().x)
print(mc.x) ## this is the syntax i am trying to achive
although mc().x works it is not dot notation.
The output i am looking for in this example would be similar to:
import myClass
data = myCalss()
print(data.x + data.y)
#>>> 3
data.set_selection("b")
print(data.x + data.y)
#>>> 7
Seem like __call__() is the wrong choice for the interface you want. Instead, maybe __getattr__() is what you want:
class i: x, y = 1, 2
class j: x, y = 3, 4
class myClass:
def __init__(self):
self.a, self.b = i(), j()
self.selection = "a"
def set_selection(self, selection):
self.selection = selection
def __getattr__(self, at):
return getattr(getattr(self, self.selection), at)
data = myClass()
print(data.x + data.y)
# 3
data.set_selection("b")
print(data.x + data.y)
# 7
Might want some checks to make sure the selection is valid.
Also, probably worth reading up on descriptors if you will be exploring this kind of stuff more deeply.

How to instantiate a subclass type variable from an existing superclass type object in Python

I have a situation where I extend a class with several attributes:
class SuperClass:
def __init__(self, tediously, many, attributes):
# assign the attributes like "self.attr = attr"
class SubClass:
def __init__(self, id, **kwargs):
self.id = id
super().__init__(**kwargs)
And then I want to create instances, but I understand that this leads to a situation where a subclass can only be instantiated like this:
super_instance = SuperClass(tediously, many, attributes)
sub_instance = SubClass(id, tediously=super_instance.tediously, many=super_instance.many, attributes=super_instance.attributes)
My question is if anything prettier / cleaner can be done to instantiate a subclass by copying a superclass instance's attributes, without having to write a piece of sausage code to manually do it (either in the constructor call, or a constructor function's body)... Something like:
utopic_sub_instance = SubClass(id, **super_instance)
Maybe you want some concrete ideas of how to not write so much code?
So one way to do it would be like this:
class A:
def __init___(self, a, b, c):
self.a = a
self.b = b
self.c = c
class B:
def __init__(self, x, a, b, c):
self.x = x
super().__init__(a, b, c)
a = A(1, 2, 3)
b = B('x', 1, 2, 3)
# so your problem is that you want to avoid passing 1,2,3 manually, right?
# So as a comment suggests, you should use alternative constructors here.
# Alternative constructors are good because people not very familiar with
# Python could also understand them.
# Alternatively, you could use this syntax, but it is a little dangerous and prone to producing
# bugs in the future that are hard to spot
class BDangerous:
def __init__(self, x, a, b, c):
self.x = x
kwargs = dict(locals())
kwargs.pop('x')
kwargs.pop('self')
# This is dangerous because if in the future someone adds a variable in this
# scope, you need to remember to pop that also
# Also, if in the future, the super constructor acquires the same parameter that
# someone else adds as a variable here... maybe you will end up passing an argument
# unwillingly. That might cause a bug
# kwargs.pop(...pop all variable names you don't want to pass)
super().__init__(**kwargs)
class BSafe:
def __init__(self, x, a, b, c):
self.x = x
bad_kwargs = dict(locals())
# This is safer: you are explicit about which arguments you're passing
good_kwargs = {}
for name in 'a,b,c'.split(','):
good_kwargs[name] = bad_kwargs[name]
# but really, this solution is not that much better compared to simply passing all
# parameters explicitly
super().__init__(**good_kwargs)
Alternatively, let's go a little crazier. We'll use introspection to dynamically build the dict to pass as arguments. I have not included in my example the case where there are keyword-only arguments, defaults, *args or **kwargs
class A:
def __init__(self, a,b,c):
self.a = a
self.b = b
self.c = c
class B(A):
def __init__(self, x,y,z, super_instance):
import inspect
spec = inspect.getfullargspec(A.__init__)
positional_args = []
super_vars = vars(super_instance)
for arg_name in spec.args[1:]: # to exclude 'self'
positional_args.append(super_vars[arg_name])
# ...but of course, you must have the guarantee that constructor
# arguments will be set as instance attributes with the same names
super().__init__(*positional_args)
I managed to finally do it using a combination of an alt constructor and the __dict__ property of the super_instance.
class SuperClass:
def __init__(self, tediously, many, attributes):
self.tediously = tediously
self.many = many
self.attributes = attributes
class SubClass(SuperClass):
def __init__(self, additional_attribute, tediously, many, attributes):
self.additional_attribute = additional_attribute
super().__init__(tediously, many, attributes)
#classmethod
def from_super_instance(cls, additional_attribute, super_instance):
return cls(additional_attribute=additional_attribute, **super_instance.__dict__)
super_instance = SuperClass("tediously", "many", "attributes")
sub_instance = SubClass.from_super_instance("additional_attribute", super_instance)
NOTE: Bear in mind that python executes statements sequentially, so if you want to override the value of an inherited attribute, put super().__init__() before the other assignment statements in SubClass.__init__.
NOTE 2: pydantic has this very nice feature where their BaseModel class auto generates an .__init__() method, helps with attribute type validation and offers a .dict() method for such models (it's basically the same as .__dict__ though).
Kinda ran into the same question and just figured one could simply do:
class SubClass(SuperClass):
def __init__(self, additional_attribute, **args):
self.additional_attribute = additional_attribute
super().__init__(**args)
super_class = SuperClass("tediously", "many", "attributes")
sub_instance = SuperClass("additional_attribute", **super_class.__dict__)

Best way to handle preset options in classes

I want to have a Class which can be initialized with options a,b and c.
c is a special case, where I can modify the initialization with a variable extend.
I'm currently looking for the best way to do this.
Also I would like my IDE (in this case PyCharm) to make suggestions to me which parameters I can use for the preset.
I came up with two ideas to do it.
Option 1:
class MyClass:
def __init__(self,preset,extend=None):
if preset == "a":
self.x = 1
if preset == "b":
self.x = 2
if preset == "c":
self.x = 3
if extend != None:
self.x = self.x + extend
def __str__(self):
return f"The value of x is {self.x}"
Y=MyClass(preset="c",extend= 3)
print(Y)
#out: The value of x is 6
Option 2:
class MyClass2:
def __init__(self):
self.x=None
def preset_a(self):
self.x=1
def preset_b(self):
self.x=2
def preset_c_with_extend(self,extend):
self.x =3+extend
def __str__(self):
return f"The value of x is {self.x}"
Y2=MyClass2()
Y2.preset_b()
print(Y2)
#out: The value of x is 2
Option 1 looks more elegant to me, but in my workflow I don't want to go to the implementation for initializing a certain preset for looking up the options.
But this would be necessary, because I can not remember for bigger projects if I named the preset a or if it was not A.
Option 1 also leaves it unclear if I can add an option extend.
Here it might happen, that I use preset a with extend=3 and I am wondering why the extend is not applied.
So the actual question: Is there an elegant way to see the preset options without looking at the class implementation? (Maybe some kind of Type Hint?)
Option 2 has this opportunity, and with auto-completion in my IDE I see what presets I can apply. But it doesn't look very elegant.
I am curious about your ideas!
How about:
class MyClass2:
def __init__(self, x):
self.x = x
#staticmethod
def preset_a():
return MyClass2(1)
#staticmethod
def preset_b():
return MyClass2(2)
#staticmethod
def preset_c_with_extend(extend):
return MyClass2(3+extend)
def __str__(self):
return f"The value of x is {self.x}"
Y2=MyClass2.preset_b()
print(Y2)
It ensures that x is set at object creation time and should allow IDE auto-completion.
Another alternative is to use a presets dict. However, I have no idea how PyCharm will treat this solution in regards to suggestions.
class MyClass:
PRESETS = {"a": 1, "b": 2, "c": 3}
def __init__(self, preset, extend=None):
self.x = self.PRESETS.get(preset)
if preset == "c" and extend is not None:
self.x += extend
def __str__(self):
return f"The value of x is {self.x}"
Note that dict's .get() method is used which means x will be None if you try to use a non-existing preset.

Is there a way to run a method for all instances of a class in python?

Example code:
>>> class MyClass(object):
def __init__(self, x, y):
self.x = x
self.y = y
def power(self):
print(self.x**self.y)
def divide(self):
print(self.x/self.y)
>>> foo = MyClass(2, 3)
>>> bar = MyClass(4, 7)
>>>
>>> foo.power()
8
>>> bar.divide()
0.5714285714285714
Whenever I used classes in Python previously, I just ran the method for each instance separately (see above). I was just wondering If there was a way to run the same method for all the instances of that class at once, because it could get a bit annoying, if you have 20 or so instances. I'm thinking of something like this:
>>> allinstances.power()
8
16384
Is there a way of doing this?
class MyClass(object):
instancelist = []
def __init__(self, x, y):
self.x = x
self.y = y
MyClass.instancelist.append(self)
def power(self):
print(self.x ** self.y)
def divide(self):
print(self.x / self.y)
foo = MyClass(2, 3)
bar = MyClass(4, 7)
[instance.power() for instance in MyClass.instancelist]
will output:
8
16384
This way you do not need any global variables or placeholders that are stored outside of the class definition.
Not usually. You could make your class be capable of that, however:
GLOBAL_MYCLASS_LIST = []
class MyClass(object):
def __init__(self, x, y):
GLOBAL_MYCLASS_LIST.append(self)
self.x = x
self.y = y
def power(self):
print(self.x**self.y)
def divide(self):
print(self.x/self.y)
a = MyClass(2, 3)
b = MyClass(4, 7)
all_powers = [i.power() for i in GLOBAL_MYCLASS_LIST]
Of course, you could also do that without baking it into the class, which is probably cleaner for most cases where you might have different sets of MyClasses:
myclass_list = []
class MyClass(object):
def __init__(self, x, y):
self.x = x
self.y = y
def power(self):
print(self.x**self.y)
def divide(self):
print(self.x/self.y)
myclass_list.append(MyClass(2, 3))
myclass_list.append(MyClass(4, 7))
all_powers = [i.power() for i in myclass_list]
From your question, I believe you would want a bit of dynamism too. Something like below could help:
I have added a function called printBoth() for demonstration.
Note: Python 2.x code below
class MyClass(object):
def __init__(self, x, y):
self.x = x
self.y = y
def power(self):
print "power",self.x**self.y
def divide(self):
print "divide", self.x/self.y
def printBoth(self):
print "x: ", self.x
print "y: ", self.y
class Test:
def __init__(self, items):
self.items = items
def __getattr__(self, key):
def fn():
return [getattr(x,key)() for x in self.items]
return fn
foo = MyClass(2, 3)
bar = MyClass(4, 7)
t = Test([foo,bar])
t.power()
t.divide()
t.printBoth()
Output:
power 8
power 16384
divide 0
divide 0
x: 2
y: 3
x: 4
y: 7
Note: The code can break on many occasions, you neeed to perform additional checks in real implementation.
The important part is here:
def __getattr__(self, key):
def fn():
return [getattr(x,key)() for x in self.items]
return fn
The above function is invoked upon any function call on the Test instance. What is returned is function that takes no arguments. The function runs over all the instances, and invokes the same function on every item on self.items (which is the list of all the instances you want to invoke your function on). The results are returned as a list.
Sure. Put the instances in a list as you create them, then iterate over the list and call the method on each instance. Also, you should change your methods to return rather than print their results, as this is much more flexible. That way you can store the results in a list, write them to a file, do further calculations with them, or print them.
instances = [MyClass(2, 3), MyClass(4, 7)]
results = [x.power() for x in instances]
Just use a loop:
foo = MyClass(2, 3)
bar = MyClass(4, 7)
for i in [foo, bar]:
i.power()
If you are not sure that all instances will have the method, use a hasattr check:
for i in list_of_instances:
if hasattr(i, 'power'):
i.power()
I think all answers are valid, depending on the use case. I would argue that if this class is used in seperated parts of your app/program you wouldn't want to depend on the GLOBAL_MYCLASS_LIST declared some where before (as #amber suggested). You would want to be sure this variable exists and declare it as a Class Variable and have the compute_all as a Class method or even as a static method with #staticmethod decorator (which I think is less relevant to the specific use case.
Again, I would stress that this is a very specific use case, and not very common. so you should think about your design before doing that and not using the more straight pythonic ways offered in other answers.
I would also like to refer you to this nice video of a Pycon lecture about classes in python which does a nice job of explaining this (The decks are here.)

Classes in Python

In Python is there any way to make a class, then make a second version of that class with identical dat,a but which can be changed, then reverted to be the same as the data in the original class?
So I would make a class with the numbers 1 to 5 as the data in it, then make a second class with the same names for sections (or very similar). Mess around with the numbers in the second class then with one function then reset them to be the same as in the first class.
The only alternative I've found is to make one aggravatingly long class with too many separate pieces of data in it to be readily usable.
A class is a template, it allows you to create a blueprint, you can then have multiple instances of a class each with different numbers, like so.
class dog(object):
def __init__(self, height, width, lenght):
self.height = height
self.width = width
self.length = length
def revert(self):
self.height = 1
self.width = 2
self.length = 3
dog1 = dog(5, 6, 7)
dog2 = dog(2, 3, 4)
dog1.revert()
Here's another answer kind of like pobk's; it uses the instance's dict to do the work of saving/resetting variables, but doesn't require you to specify the names of them in your code. You can call save() at any time to save the state of the instance and reset() to reset to that state.
class MyReset:
def __init__(self, x, y):
self.x = x
self.y = y
self.save()
def save(self):
self.saved = self.__dict__.copy()
def reset(self):
self.__dict__ = self.saved.copy()
a = MyReset(20, 30)
a.x = 50
print a.x
a.reset()
print a.x
Why do you want to do this? It might not be the best/only way.
Classes don't have values. Objects do. Is what you want basically a class that can reset an instance (object) to a set of default values?
How about just providing a reset method, that resets the properties of your object to whatever is the default?
I think you should simplify your question, or tell us what you really want to do. It's not at all clear.
I think you are confused. You should re-check the meaning of "class" and "instance".
I think you are trying to first declare a Instance of a certain Class, and then declare a instance of other Class, use the data from the first one, and then find a way to convert the data in the second instance and use it on the first instance...
I recommend that you use operator overloading to assign the data.
class ABC(self):
numbers = [0,1,2,3]
class DEF(ABC):
def __init__(self):
self.new_numbers = super(ABC,self).numbers
def setnums(self, numbers):
self.new_numbers = numbers
def getnums(self):
return self.new_numbers
def reset(self):
__init__()
Just FYI, here's an alternate implementation... Probably violates about 15 million pythonic rules, but I publish it per information/observation:
class Resettable(object):
base_dict = {}
def reset(self):
self.__dict__ = self.__class__.base_dict
def __init__(self):
self.__dict__ = self.__class__.base_dict.copy()
class SomeClass(Resettable):
base_dict = {
'number_one': 1,
'number_two': 2,
'number_three': 3,
'number_four': 4,
'number_five': 5,
}
def __init__(self):
Resettable.__init__(self)
p = SomeClass()
p.number_one = 100
print p.number_one
p.reset()
print p.number_one

Categories