I work on a project with a huge class. Initially they were implemented as functions that just get imported, like in this answer:
def plot(self, x, y):
print(self.field)
def clear(self):
# body
#classmethod
def from_file(cls, path):
# body
class Fitter(object):
def __init__(self, whatever):
self.field = whatever
# Imported methods
from .some_file import plot, clear, from_file
...
But I think it's not the best solution, IDE is mad on the code because it cannot find field in the external methods and considers classmethod on some functions as an error. I hope mixins can help with it.
But I see the similar problem in this approach: mixin classes don't have a base class with all the common methods and fields specified (an example in Django), so IDE and linters cannot find definitions and understand the code correctly too... I tried to use some common base class, as the following:
class FitterBase(object):
def __init__(self, whatever):
self.field = whatever
class FiterMixin(FitterBase):
def plot(self, x, y):
print(self.field)
def clear(self):
# body
#classmethod
def from_file(cls, path):
# body
class Fitter(FitterBase, FiterMixin):
pass
But the interpreter raises an error:
TypeError: Cannot create a consistent method resolution
Is there any solution to this problem? It's really important because the class contains dozens of big methods, and correct inheritance would help a lot.
Python is trying to construct an MRO for Fitter in which FitterBase both precedes and follows FitterMixin; the former because FitterBase is listed first in the seance of base classes, the latter because it is the parent of FitterMixin.
To resolve that issue, simply swap the order of the two base classes:
class Fitter(FitterMixin, FitterBase):
pass
There's no reason to list both, though, because FitterMixin already inherits from FitterBase:
class Fitter(FitterMixin):
pass
As this makes more obvious, FitterMixin isn't really a mix-in class, because you aren't mixing it with anything. Or, don't make FitterMixin subclass FitterBase:
class FitterBase:
def __init__(self, whatever):
self.field = whatever
class FitterMixin:
def plot(self, x, y):
print(self.field)
def clear(self):
pass
#classmethod
def from_file(cls, path):
pass
class Fitter(FitterBase, FitterMixin):
pass
Related
I have a need to run some code at class creation time, invoking a function (in this case it happens to be a method) that I need to pass the cls class object and a few other things (mostly defined in the parent).
My solution so far is this:
#PostConstruct()
class Child(Parent):
X = 1
Y = Parent.A
Z = 2
#classmethod
def __post_construct__(cls):
cls.add_thing(cls.X, as_key=True, before=cls.Y)
cls.add_thing(cls.Z, as_key=False, before=cls.Y)
Supporting code:
class PostConstruct:
"""
Runs a class's ``__post_construct__`` class method immediately after
the body code of the class is run. Allows an author to make small
modifications to the class (e.g. modifying class-level variables) at
class creation time.
"""
def __call__(self, cls):
cls.__post_construct__()
return cls
class Parent:
A = 0
#classmethod
def add_thing(cls, thing, as_key, before):
print("Adding thing...")
Is there some built-in post-class-construction hook method I've overlooked, so I wouldn't need to write this decorator myself? I've looked at https://docs.python.org/3/reference/datamodel.html#customizing-class-creation but I haven't seen anything that seems relevant. But this wouldn't be the first time I've implemented some "clever" thing and then learned later that I could have done it simpler.
Or any other suggestion to achieve a similar result?
Thanks, all. Putting together the suggested tweaks from the comments, here's how I'm moving forward:
#post_construct()
class Child(Parent):
X = 1
Y = Parent.A
Z = 2
#classmethod
def _post_construct(cls):
cls.add_thing(cls.X, as_key=True, before=cls.Y)
cls.add_thing(cls.Z, as_key=False, before=cls.Y)
Supporting code:
def post_construct(cls):
"""
Runs a class's ``_post_construct`` class method immediately after the body code of the class is run. Allows an author to make
small modifications to the class (e.g. modifying class-level variables) at class creation time.
"""
cls._post_construct()
return cls
class Parent:
A = 0
#classmethod
def add_thing(cls, thing, as_key, before):
print("Adding thing...")
TLDR;
I am using a #classmethod as a constructor for my class, and I need to override it with a different signature for one specific child class that needs extra parameters. PyCharm gives a warning about overriding a method with different signature. I wonder whether it also applies to #classmethod constructors.
I am using the IDE PyCharm for my python project and I have received the following warning regarding the overriding of a method in a class:
Signature of method [...] does not match signature of base method in class [...]
I understand this is related to the Liskov substitution principle, meaning objects of a parent class should always be replaceable by objects of a child class.
However, in my case I am overriding a #classmethod which is used as a constructor, following some sort of factory pattern. A simplification of my code would be as follows:
class Parent:
def __init__(self, common, data):
self.common = common
self.data = data
#classmethod
def from_directory(cls, data_dir, common):
all_data = [load_data(data_file) for data_file in get_data_files(data_dir)]
return [cls(common, data) for data in all_data]
class ChildA(Parent):
def __init__(self, common, data, specific):
super().__init__(common, data)
self.specific = specific
#classmethod
def from_directory(cls, data_dir, common, specific):
all_data = [load_data(data_file) for data_file in get_data_files(data_dir)]
return [cls(common, data, specific) for data in all_data]
In this example, basically I have a parent class Parent with some common attribute that all child classes will inherit, and some particular child class ChildA which has an extra, subclass-specific attribute.
Since I am using the #classmethod as a constructor, I assume the Liskov principle does not apply, just in the same way that the __init__() method can be overridden with a different signature. However, the PyCharm warning has made me consider whether there is something I might have missed. I am not sure whether I am using the #classmethod in a sensitive way.
My main question is then: Is PyCharm being overzealous with its warnings here or is there any reason the pattern described above should be avoided?
Also, any feedback about any other design issues / misconceptions I might have is most welcome.
I would refine your class method. There are really two class methods to provide here: one that creates an instance of the class from a data file, and one that produces a list of instances from the files in a directory (using the first class method). Further, the class methods shouldn't care about which arguments cls will need: it just passes on whatever it receives (with the exception of data, which it knows about and will provide or override with whatever it reads from a file).
class Parent:
def __init__(self, common, data, **kwargs):
super().__init__(**kwargs)
self.common = common
self.data = data
#classmethod
def from_file(cls, filename, **kwargs):
# If the caller provided a data argument,
# ignore it and use the data from the file instead.
kwargs['data'] = load_data(filename)
return cls(**kwargs)
#classmethod
def from_directory(cls, data_dir, **kwargs):
return [cls.from_file(data_file, **kwargs)
for data_file in get_data_files(data_dir)]
class ChildA(Parent):
def __init__(self, specific, **kwargs):
super().__init__(**kwargs)
self.specific = specific
Notice that you no longer need to override Parent.from_directory; it's already agnostic about what arguments it receives that are intended for __init__.
I've made a class which can be compared and sorted inside common data structures.
The thing is that I wanted to make two class constants for the maximum and minimum values that class can take. So I could call this value just importing MyClass and writing
obj = MyClass.MY_MAX_CONSTANT
The thing is that calling the constructor or init method to initialize these constants is not allowed.
In Java this would be declared as static and it would work, but I don't know how can I do a class / static constant in Python using the constructor / init method. Haven't found much googling but some general recipes for constants and suggestions for making properties.
I don't need a mechanism to avoid changing the constant value since I'm definitely not changing it.
My first try was:
class MyClass(object):
MY_MAX_CONSTANT = MyClass(10,10)
MY_MIN_CONSTANT = MyClass(0,0)
def __init__(self, param1, param2): # Not the exact signature, but I think this works as an example
# We imagine some initialization work here
self.x = param1
self.y = param2
# SORT FUNCTIONS
def __cmp__(self, other):
# Implementation already made here
def __eq__(self, other):
# Implementation already made here
def __ne__(self, other):
# Implementation already made here
def __ge__(self, other):
# Implementation already made here
# And so on...
A second try, by using some functions for each constant:
class MyClass(object):
def __init__(self, param1, param2): # Not the exact signature, but I think this works as an example
# We imagine some initialization work here
self.x = param1
self.y = param2
MY_MAX_CONSTANT = None
MY_MIN_CONSTANT = None
#staticmethod
def get_max(self):
if not MyClass.MY_MAX_CONSTANT:
MyClass.MY_MAX_CONSTANT = MyClass(10,10)
return MyClass.MY_MAX_CONSTANT
#staticmethod
def get_min(self):
if not MyClass.MY_MIN_CONSTANT:
MyClass.MY_MIN_CONSTANT = MyClass(0,0)
return MyClass.MY_MIN_CONSTANT
# SORT FUNCTIONS (I'm not writing them twice for spacing)
But I wanted to avoid strange function mechanisms only for making two constants.
I prefer the constant being in the class and not a module because it feels more natural to me, but I'm hearing any advice or suggestion. Can anyone point me a better pythonic solution?
Thanks
Add your constants after creating your class, you are allowed to add more class attributes:
class MyClass:
# ...
MyClass.MY_MAX_CONSTANT = MyClass(10, 10)
MyClass.MY_MIN_CONSTANT = MyClass(0, 0)
Only when the class statement has completed running is the class object available, bound to the name MyClass. You can't create instances before this point.
I am just wondering if there is a shortcut to setting default attributes.
I generally would do
class class1(object):
def __init__(self, att1='X'):
self.att1 = att1
Is there a shortcut in the lines off
class class1(object):
def __init__(self, self.att1='X'):
I assume at the moment of the call, the object does not exist, so seem logical that this way does not work, but maybe there is sort of less verbose way to deal with this when there is a lot more attributes to be set.
Any ideas?
Try something like:
class class1(object):
att1='X'
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
I want to be able to create an instance of a parent class X, with a string "Q" as an extra argument.
This string is to be a name being an identifier for a subclass Q of the parent class X.
I want the instance of the parent class to become (or be replaced with) an instance of the subclass.
I am aware that this is probably a classic problem (error?). After some searching I haven't found a suitable solution though.
I came up with the following solution myself;
I added a dictionary of possible identifiers as keys for their baseclass-instances to the init-method of the parent class.
Then assigned the class-attribute of the corresponding subclass to the current instances class-attribute.
I required the argument of the init-method not to be the default value to prevent infinite looping.
Following is an example of what the code looks like in practice;
class SpecialRule:
""""""
name="Special Rule"
description="This is a Special Rule."
def __init__(self, name=None):
""""""
print "SpecialInit"
if name!=None:
SPECIAL_RULES={
"Fly" : FlyRule(),
"Skirmish" : SkirmishRule()
} #dictionary coupling names to SpecialRuleclasses
self.__class__= SPECIAL_RULES[name].__class__
def __str__(self):
""""""
return self.name
class FlyRule(SpecialRule):
""""""
name="Fly"
description="Flies."
def __init__(self):
""""""
print "FlyInit"+self.name
SpecialRule.__init__(self)
def addtocontainer(self, container):
"""this instance messes with the attributes of its containing class when added to some sort of list"""
class SkirmishRule(SpecialRule):
""""""
name="Skirmish"
description="Skirmishes."
def __init__(self):
""""""
SpecialRule.__init__(self)
def addtocontainer(self, container):
"""this instance messes with the attributes of its containing class when added to some sort of list"""
test=SpecialRule("Fly")
print "evaluating resulting class"
print test.description
print test.__class__
</pre></code>
output:
>
SpecialInit
FlyInitFly
SpecialInit
evaluating resulting class
Flies.
main.FlyRule
>
Is there a more pythonic solution and are there foresee-able problems with mine?
(And am I mistaken that its a good programming practice to explicitly call the .__init__(self) of the parent class in .__init__ of the subclass?).
My solution feels a bit ... wrong ...
Quick recap so far;
Thanks for the quick answers
# Mark Tolonen's solution
I've been looking into the __new__-method, but when I try to make A, B and C in Mark Tolonen's example subclasses of Z, I get the error that class Z isn't defined yet. Also I'm not sure if instantiating class A the normal way ( with variable=A() outside of Z's scope ) is possible, unless you already have an instance of a subclass made and call the class as an attribute of an instance of a subclass of Z ... which doesn't seem very straightforward. __new__ is quite interesting so I'll fool around with it a bit more, your example is easier to grasp than what I got from the pythondocs.
# Greg Hewgill's solution
I tried the staticmethod-solution and it seems to work fine. I looked into using a seperate function as a factory before but I guessed it would get hard to manage a large program with a list of loose strands of constructor code in the main block, so I'm very happy to integrate it in the class.
I did experiment a bit seeing if I could turn the create-method into a decorated .__call__() but it got quite messy so I'll leave it at that.
I would solve this by using a function that encapsulates the choice of object:
class SpecialRule:
""""""
name="Special Rule"
description="This is a Special Rule."
#staticmethod
def create(name=None):
""""""
print "SpecialCreate"
if name!=None:
SPECIAL_RULES={
"Fly" : FlyRule,
"Skirmish" : SkirmishRule
} #dictionary coupling names to SpecialRuleclasses
return SPECIAL_RULES[name]()
else:
return SpecialRule()
I have used the #staticmethod decorator to allow you to call the create() method without already having an instance of the object. You would call this like:
SpecialRule.create("Fly")
Look up the __new__ method. It is the correct way to override how a class is created vs. initialized.
Here's a quick hack:
class Z(object):
class A(object):
def name(self):
return "I'm A!"
class B(object):
def name(self):
return "I'm B!"
class C(object):
def name(self):
return "I'm C!"
D = {'A':A,'B':B,'C':C}
def __new__(cls,t):
return cls.D[t]()