class parent:
#abstractmethod
def process(self, x, y, z):
pass
def post_process(self):
self.process(x, y, z)
class child1(parent):
def process(self, x, y, z):
# do stuff with x and y, and ignore z
class child1(parent):
def process(self, x, y, z):
# do stuff with x, y, z
This is an existing design in the codebase I am working in. I don't like it for readability because the variable z isn't used for all child classes, and I suspect it was designed this way so that post_process doesn't need to check which child class is calling.
I am thinking of changing it to:
class parent:
def post_process(self):
if isinstance(self, child1):
self.process(x, y)
elif isinstance(self, child2):
self.process(x, y, z)
class child1(parent):
def process(self, x, y):
# do stuff with x and y
class child1(parent):
def process(self, x, y, z):
# do stuff with x, y, z
This does the case work of checking which child class is calling post_process, but I feel it is better for readability and avoids having to pass in unnecessary arguments.
Are there other ways besides this to resolve this issue?
Related
I have a class a defined like this:
class a:
def __init__(self, w, x, y, z):
self.w = w
self.x = x
self.y = y
self.__z = z
I also have another class b which inherits a defined like this:
class b(a):
def __init__(self, w, x, y, z, t):
super().__init__(w, x, y, z)
self.__t = t
Now if I had access w, x, y from within b, I could simply do:
self.w
self.x
self.y
But I can't do self.z or self.__z to access z. So my question is how can you access dunder values such as z from within class b
(I know python doesn't really have private variables and I could do self._a__z from within class b to access z but I'm looking for methods which would allow me to just do something like self.z to access z from inside b)
There are a variety of ways you could solve the problem by changing the API of class a to expose it's __z attribute in some more inheritance-friendly way. For instance, you could make the actual mangled attribute name an implementation detail and have the public API be a property:
class a:
def __init__(self, w, x, y, z):
self.w = w
self.x = x
self.y = y
self.__z = z
#property
def z(self):
return self.__z
#z.setter
def z(self, value):
self.__z = value
But unless you're doing some validation or modification of the value somewhere in the property's code, you probably should just do away with the double-underscores and let the child class access self.z like it can w, x and y. The simplest solution is to replace self.__z = z with self.z = z. You can always change it later to use a property if you find you do need validation or something.
If you just want to hint that z is not part of the wider public API, but only for subclasses and other code that knows about the intimate details of a's design, consider using a single underscore: self._z = z. This has no special effects in the interpreter, but the single-underscore serves as a sort of documentation that _z is an internal attribute and you should only mess with it if you know what you're doing.
You could do something like this if you want to keep your code clean:
class a:
def __init__(self, w, x, y, z):
self.w = w
self.x = x
self.y = y
self.__z = z
def get_field(self):
return self.__z
class b(a):
def __init__(self, w, x, y, z, t):
super().__init__(w, x, y, z)
self.__t = t
def ChildMethodWhichUsingParentField(self):
return self.get_field()
c = b(1,2,3,4,5)
print(c.ChildMethodWhichUsingParentField())
Output
4
Similarly, you can use a setter to change its value:
class a:
def __init__(self, w, x, y, z):
self.w = w
self.x = x
self.y = y
self.__z = z
def get_field(self):
return self.__z
def set_field(self,z):
self.__z = z
class b(a):
def __init__(self, w, x, y, z, t):
super().__init__(w, x, y, z)
self.__t = t
def ChildMethodWhichUsingParentField(self):
return self.get_field()
def ChildMethodWhichSettingParentField(self,z):
self.set_field(z)
c = b(1,2,3,4,5)
print(c.ChildMethodWhichUsingParentField())
c.ChildMethodWhichSettingParentField(10)
print(c.ChildMethodWhichUsingParentField())
Output
4
10
Below is a MWE of my attempt to change the signature of MyClass.func from (self, a, b, c) to (self, x, y, z). As you can see from the outputs, the change_sig decorator works on functions and also works on the bound method MyClass().func, but fails on the unbound method. Is there some sort of magic going on behind the scenes when I assign a callable attribute to a class that keeps this from working?
MWE:
import wrapt
import inspect
def change_sig(func):
adapter = '(self, x, y, z)'
#wrapt.decorator(adapter=adapter)
def wrapper(wrapped, instance, args, kwargs):
pass
wrapped_func = wrapper(func)
return wrapped_func
#change_sig
def func(self, a, b, c):
pass
class MyClass:
#change_sig
def func(self, a, b, c):
pass
print('inspect.signature(func):', inspect.signature(func))
print('inspect.signature(MyClass.func):', inspect.signature(MyClass.func))
print('inspect.signature(MyClass().func):', inspect.signature(MyClass().func))
print('inspect.signature(change_sig(MyClass.func)):', inspect.signature(change_sig(MyClass.func)))
MyClass.wrapped_func = change_sig(MyClass.func)
print('inspect.signature(MyClass.wrapped_func):', inspect.signature(MyClass.wrapped_func))
Output:
inspect.signature(func): (self, x, y, z)
inspect.signature(MyClass.func): (self, a, b, c)
inspect.signature(MyClass().func): (x, y, z)
inspect.signature(change_sig(MyClass.func)): (self, x, y, z)
inspect.signature(MyClass.wrapped_func): (self, a, b, c)
This was traced to a bug in wrapt, which is being addressed: https://github.com/GrahamDumpleton/wrapt/issues/148
I have a parent class:
class BaseClass:
def __init__(self, foo, bar=10):
self.foo = foo
And an inherited class from it:
class InheritedClass(BaseClass):
def __init__(self, x, y, bar=10):
super().__init__(True, bar=bar)
self.x = x
self.y = y
At the moment, bar has a default value in both the parent and the inherited class, which I have to keep track of to make sure they are both synced. How can I change the InheritedClass constructor to instead use the default value for bar defined in the BaseClass?
I've tried:
class InheritedClass(BaseClass):
def __init__(self, x, y, bar=None):
if bar is None:
super().__init__(True)
else:
super().__init__(True,bar)
self.x = x
self.y = y
which provides the behavior I'm looking for, but doesn't seem very scalable. Is there a better way?
Don't expose the parent's arguments at all in the signature; just pass what you receive on (if anything) on to the next class in the MRO.
class BaseClass:
def __init__(self, foo, bar=10, **kwargs):
super().__init__(**kwargs)
# Do something with bar
self.foo = foo
class InheritedClass(BaseClass):
def __init__(self, x, y, **kwargs)
kwargs['foo'] = True
super().__init__(**kwargs)
self.x = x
self.y = y
You have to accept and pass on arbitrary keywords anyway to use super properly, so you may as well do the same thing for the parent class's arguments, even though you "know" them already.
This also means always using keyword arguments for __init__, as you can't really predict how your class's positional arguments will interact with another class on the MRO. (Positional arguments can only be consumed on a first-come, first-served basis, and you simply don't know in the case of multiple inheritance when your class's method will be called.)
foo = InheritedClass(bar=9, x=3, y=10)
You can use variable arguments instead:
class InheritedClass(BaseClass):
def __init__(self, x, y, *args, **kwargs):
super().__init__(True, *args, **kwargs)
self.x = x
self.y = y
Your approach is not bad and yes there is a better way to do it maybe better than mine but here is what I come up with hope this helps.
Sample:
class BaseClass:
def __init__(self, foo, bar=10):
self.foo = foo
self.bar = bar
class InheritedClass(BaseClass):
def __init__(self, x, y, bar=None):
super().__init__(True, bar=bar)
if bar is None: bar = self.bar
self.x = x
self.y = y
Say I have a class definition which takes some arguments and creates additional data with it, all within the __init__ method:
class Foo():
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
self.bar = generate_bar(x, y, z)
def generate_bar(self, x, y, z):
return x+y+z
I only want to run the generate_bar() method once, when an instance of the class is created, so it wouldn't make sense for that method to be callable on the instance. Is there a sleeker way to ensure that a method is only available to __init__, or do I just have to assume anyone who looks at my code will understand that there's never a situation in which it would be appropriate to call generate_bar() on an instance of the class?
If you are not using any instance state, just make it a separate function:
def _generate_bar(x, y, z):
return x + y + z
class Foo():
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
self.bar = _generate_bar(x, y, z)
The leading underscore, by convention, signals it is an internal function not to be used by external consumers of your module.
You could nest the function inside the __init__ but this doesn't really help with readability:
class Foo():
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def generate_bar():
return x + y + z
self.bar = generate_bar()
Here generate_bar() doesn't even need arguments, it could access x, y and z from the enclosing scope.
For "hidden" functions, it is customary to use a single underscore to signify your intent:
def _generate_bar(self, x, y, z): ...
These are still accessible, but are not visible via tab completion.
See this SO explanation:
class foo:
def __init__(self):
self.response = self._bar()
def _bar(self):
return "bar"
f = foo()
>>> f.response
bar
You can verify yourself that the function _bar is not visible to the object via tab completion
I am often using classmethods instead of the default constructor in python for example:
class Data(object):
def __init__(self, x, y, z):
self.x = x etc..
#classmethod
def from_xml(cls, xml_file):
x, y, z = import_something_from_xml(xml_file)
return cls(x,y,z)
this approach works good,
but since i often have large classmethod-constructors I want to split them up in smaller functions. My problem with that is, that these smaller functions can be seen in the Class namespace, Is there any way to avoid this ?
You can mark the smaller helper functions as private:
#classmethod
def __import_something_from_xml(cls, data):
#logic
return a, b, c
and you would run:
#classmethod
def from_xml(cls, xml_file):
x, y, z = cls.__import_something_from_xml(xml_file)
return cls(x,y,z)
Keep in mind this is only naming convention and this method can be accessed from Data namespace.
Or you can designate a helper class:
class XMLDataHelper:
#staticmethod
def import_something_from_xml(data):
#logic
return a, b, c
And the code would look like this
#classmethod
def from_xml(cls, xml_file):
x, y, z = XMLDataHelper.import_something_from_xml(xml_file)
return cls(x,y,z)