How to dynamically determine which Python subclass to create? - python

Say I have a Word class with subclasses ShortWord and LongWord. Is there a way to dynamically determine which subclass gets instantiated based on a given input? I mean I could define a separate function like
def instantiate(text):
if len(text) < 5:
return ShortWord(text)
else:
return LongWord(text)
But that doesn't feel very (for lack of a better word) elegant. Is there a good way to go about this? I have found some similar questions in other languages, but nothing python-specific.

I would define a #staticmethod in the base class which makes the determination.
You might need to define a __init_subclass__ in the base class to at least be aware of what classes are there, since you don't know about the subclasses before the base class is defined.
class Word(object):
_tr = {} # Type registry
#staticmethod
def instantiate(text):
if len(text) < 5:
cls = __class__._tr.get('ShortWord')
else:
cls = __class__._tr.get('LongWord')
return cls(text) if cls is not None else None
def __init_subclass__(cls):
__class__._tr[cls.__name__] = cls
class ShortWord(Word):
pass
class LongWord(Word):
pass
I thought it over after seeing the comments, and this is another approach. Didn't want to submit two answers for one question. UPDATE: Thanks to #chepner's comment, I cleaned it up further.
class Word(object):
#staticmethod
def instantiate(text):
if len(text) < 5:
return ShortWord(text)
else:
return LongWord(text)
class ShortWord(Word):
pass
class LongWord(Word):
pass
Instantiate with val = Word.instantiate(text)

Related

Encapsulate the decision which child class to initialize

I have a parent class and different child classes. I want to encapsulate the decision which child class is to initialize in the initialization.
A simple example:
class Person:
def __init__(self, name):
if self.name_is_male(name):
real_instance = Male(name)
else:
real_instance = Female(name)
return real_instance
def name_is_male(self, name):
if name == 'Donald':
return True
elif name == 'Daisy':
return False
else:
raise ValueError('unknown name!')
class Male(Person):
def __init__(self, name):
...
class Female(Person):
def __init__(self, name):
...
This simple example will end in a recursion and doesn’t work, but it’s for illustrating my question: how to encapsulate the decision which child class to initialize in the initialization of a parent class? Or is this altogether a stupid idea?
Though the use case is not very clear, I would have used factory design pattern to achieve something similar to this. A basic example can be:
class Person(object):
# Create objects based on some name:
#staticmethod
def factory(name):
if name== "Male":
return Male()
elif name== "Female":
return Female()
else:
return None
class Male(Person):
pass
class Female(Person):
pass
person = Person.factory('Male')
Another example on factory method design pattern
__init__ is not supposed to return anything (or rather: it has to return None). Imo it's not the best way of writing it, or as you put it "altogether a stupid idea". Is there a particular reason why it can't be an attribute?

python classes getters and setters static method and class method

class Spam(object):
#a_string = 'candy'
def __init__(self, sold=0, cost=0):
self.sold = sold
self.cost = cost
#staticmethod
def total_cost():
return True
#classmethod
def items_sold(cls, how_many):
#property
def silly_walk(self):
return print (self.a_string)
#silly_walk.setter
def silly_walk(self, new_string):
self.a_string = new_string.upper()
def do_cost(self):
if self.total_cost():
print('Total cost is:', self.cost)
.
from spam import Spam
def main ():
cost = 25
sold = 100
a_string = 'sweets'
sp = Spam(100, 25)
sp.do_cost()
sw = Spam.silly_walk(a_string)
sw.silly_walk()
if __name__ == '__main__':
main()
so im new to python and i don't understand how to use the setters and getters in this. so what i want to do is:
use #property to create a setter and getter for a property named silly_walk. Have the setter upper case the silly_walk string.
Show example code that would access the static method.
Show example code that would use the silly_walk setter and getter.
im getting very confused with what "self" does in the class and im not sure if what im doing is correct
update:
problem was the #classmethod not having a return and indentation error, so everything is fixed thanks everybody
self is convention. Since you're inside a class, you don't have functions there you have methods. Methods expect a reference to the object calling them as the first argument, which by convention is named self. You can call it anything you like.
class Foo(object):
def __init__(itsa_me_maaaario, name):
itsa_me_maaario.name = "Mario"
That works just as well.
As for the rest of your code -- what's your QUESTION there? Looks like your setter is a bit weird, but other than that it should work mostly okay. This is better:
class Spam(object): # inherit from object in py2 for new-style classes
def __init__(self, a_string, sold=0, cost=0) # put the positional arg first
...
#staticmethod
def total_cost():
# you have to do something meaningful here. A static method can't access
# any of the objects attributes, it's really only included for grouping
# related functions to their classes.
#classmethod
def items_sold(cls, how_many):
# the first argument to a classmethod is the class, not the object, so
# by convention name it cls. Again this should be something relevant to
# the class not to the object.
#property
def silly_walk(self):
return self.a_string
# don't call itself.
#silly_walk.setter
def silly_walk(self, new_string):
self.a_string = new_string
# it really just hides the attribute.
For instance I have a class I built to abstract a computer system I'm in charge of. It might be something like:
class System(object):
type_ = "Base system"
def __init__(self, sitenum, devicenum, IP):
self._sitenum = sitenum
self._devicenum = devicenum
self._IP = IP
# the leading underscores are a flag to future coders that these are
# "private" variables. Nothing stopping someone from using it anyway,
# because System()._IP is still that attribute, but it makes it clear
# that they're not supposed to be used that way.
#staticmethod
def ping_system(IP):
subprocess.call(["ping",IP], shell=True) # OH GOD SECURITY FLAW HERE
# group this with Systems because maybe that's how I want it? It's an
# aesthetic choice. Note that this pings ANY system and requires an
# argument of an IP address!
#classmethod
def type_of_system(cls):
return cls.type_
# imagine I had a bunch of objects that inherited from System, each w/
# a different type_, but they all inherit this....
#property
def description(self):
return "Site {}, Device {} # {}".format(self._sitenum,
self._devicenum,
self._IP)
#description.setter
def description(self, *args):
if len(args) == 3:
self._sitenum, self._devicenum, self._IP = args
elif len(args) == 1 and len(args[0]) == 3:
self._sitenum, self._devicenum, self._IP = args[0]
else:
raise ValueError("Redefine description as Sitenum, Devicenum, IP")
Example:
computer = System(1, 1, '192.168.100.101')
System.ping_system('192.160.100.101') # works
computer.type_of_system # "Base system"
computer.description # "Site 1, Device 1 # 192.168.100.101"
new_description = [1, 2, '192.168.100.102']
computer.description = new_description
# invokes description.setter
computer._devicenum # is 2 after the setter does its magic.

Should a class constructor return a subclass?

Should a class constructor return a subclass?
This is mostly a question about OOP style and python style. I have problem where I need to implement a general case solution and, for performance reasons, I need to implement an optimized solution for a specific input type. The input type depends on the user. Currently I've implemented this by sub-classing the general case solution to make the optimized solution. I've come up with the following example to help describe what I mean.
from collections import Counter
class MyCounter(object):
"""General Case Counter"""
def __init__(self, seq):
self.seq = seq
def count(self, key):
return sum(key == item for item in self.seq)
class OptimizedCounter(MyCounter):
"""Counter optimized for hashable types"""
def __init__(self, seq):
self.counter = Counter(seq)
def count(self, key):
return self.counter.get(key, 0)
counter = MyCounter(['a', 'a', 'b', [], [0, 1]])
counter.count([0, 1])
# 1
counter = OptimizedCounter(['a', 'a', 'b'])
counter.count('a')
# 2
My question is how do I design a smooth interface so that the user gets an appropriate instance without having to worry about how it's implemented. I've considered doing something like the following, but that feels ugly to me. Is there a more canonical or OOP way to do something like this?
class MyCounter(object):
"""General Case Counter"""
def __new__(cls, seq):
if hasOnlyHashables(seq):
return object.__new__(OptimizedCounter)
else:
return object.__new__(MyCounter)
Use a factory function that returns an instance of the appropriate class.
def makeCounter(seq):
if hasOnlyHashables(seq):
return OptimizedCounter(seq)
else:
return MyCounter(seq)
Your allocator implementation is a little off. If you need to create an instance of a child (or different) type then you do so by calling its constructor; only if you want to create an instance of the current class should you call the parent's (object in this case) allocator.
No, the class constructor doesn't return anything.
You need to create a factory as suggested by BrenBarn, but I would put that factory as a static method in the most generic class.
Something like this:
class MyCounter(object):
#staticmethod
def from_seq(seq):
if hasOnlyHashables(seq):
return OptimizedCounter(seq)
else:
return MyCounter(seq)

Virtual classes: doing it right?

I have been reading documentation describing class inheritance, abstract base classes and even python interfaces. But nothing seams to be exactly what I want. Namely, a simple way of building virtual classes. When the virtual class gets called, I would like it to instantiate some more specific class based on what the parameters it is given and hand that back the calling function. For now I have a summary way of rerouting calls to the virtual class down to the underlying class.
The idea is the following:
class Shape:
def __init__(self, description):
if description == "It's flat": self.underlying_class = Line(description)
elif description == "It's spiky": self.underlying_class = Triangle(description)
elif description == "It's big": self.underlying_class = Rectangle(description)
def number_of_edges(self, parameters):
return self.underlying_class(parameters)
class Line:
def __init__(self, description):
self.desc = description
def number_of_edges(self, parameters):
return 1
class Triangle:
def __init__(self, description):
self.desc = description
def number_of_edges(self, parameters):
return 3
class Rectangle:
def __init__(self, description):
self.desc = description
def number_of_edges(self, parameters):
return 4
shape_dont_know_what_it_is = Shape("It's big")
shape_dont_know_what_it_is.number_of_edges(parameters)
My rerouting is far from optimal, as only calls to the number_of_edges() function get passed on. Adding something like this to Shape doesn't seam to do the trick either:
def __getattr__(self, *args):
return underlying_class.__getattr__(*args)
What I am doing wrong ? Is the whole idea badly implemented ? Any help greatly appreciated.
I agree with TooAngel, but I'd use the __new__ method.
class Shape(object):
def __new__(cls, *args, **kwargs):
if cls is Shape: # <-- required because Line's
description, args = args[0], args[1:] #  __new__ method is the
if description == "It's flat": # same as Shape's
new_cls = Line
else:
raise ValueError("Invalid description: {}.".format(description))
else:
new_cls = cls
return super(Shape, cls).__new__(new_cls, *args, **kwargs)
def number_of_edges(self):
return "A shape can have many edges…"
class Line(Shape):
def number_of_edges(self):
return 1
class SomeShape(Shape):
pass
>>> l1 = Shape("It's flat")
>>> l1.number_of_edges()
1
>>> l2 = Line()
>>> l2.number_of_edges()
1
>>> u = SomeShape()
>>> u.number_of_edges()
'A shape can have many edges…'
>>> s = Shape("Hexagon")
ValueError: Invalid description: Hexagon.
I would prefer doing it with a factory:
def factory(description):
if description == "It's flat": return Line(description)
elif description == "It's spiky": return Triangle(description)
elif description == "It's big": return Rectangle(description)
or:
def factory(description):
classDict = {"It's flat":Line("It's flat"), "It's spiky":Triangle("It's spiky"), "It's big":Rectangle("It's big")}
return classDict[description]
and inherit the classes from Shape
class Line(Shape):
def __init__(self, description):
self.desc = description
def number_of_edges(self, parameters):
return 1
Python doesn't have virtual classes out of the box. You will have to implement them yourself (it should be possible, Python's reflection capabilities should be powerful enough to let you do this).
However, if you need virtual classes, then why don't you just use a programming language which does have virtual classes like Beta, gBeta or Newspeak? (BTW: are there any others?)
In this particular case, though, I don't really see how virtual classes would simplify your solution, at least not in the example you have given. Maybe you could elaborate why you think you need virtual classes?
Don't get me wrong: I like virtual classes, but the fact that only three languages have ever implemented them, only one of those three is still alive and exactly 0 of those three are actually used by anybody is somewhat telling …
You can change the class with object.__class__, but it's much better to just make a function that returns an instance of an arbitrary class.
On another note, all class should inherit from object unless you use using Python 3, like this, otherwise you end up with an old-style class:
class A(object):
pass

How to implement property() with dynamic name (in python)

I am programming a simulations for single neurons. Therefore I have to handle a lot of Parameters. Now the Idea is that I have two classes, one for a SingleParameter and a Collection of parameters. I use property() to access the parameter value easy and to make the code more readable. This works perfect for a sinlge parameter but I don't know how to implement it for the collection as I want to name the property in Collection after the SingleParameter. Here an example:
class SingleParameter(object):
def __init__(self, name, default_value=0, unit='not specified'):
self.name = name
self.default_value = default_value
self.unit = unit
self.set(default_value)
def get(self):
return self._v
def set(self, value):
self._v = value
v = property(fget=get, fset=set, doc='value of parameter')
par1 = SingleParameter(name='par1', default_value=10, unit='mV')
par2 = SingleParameter(name='par2', default_value=20, unit='mA')
# par1 and par2 I can access perfectly via 'p1.v = ...'
# or get its value with 'p1.v'
class Collection(object):
def __init__(self):
self.dict = {}
def __getitem__(self, name):
return self.dict[name] # get the whole object
# to get the value instead:
# return self.dict[name].v
def add(self, parameter):
self.dict[parameter.name] = parameter
# now comes the part that I don't know how to implement with property():
# It shoule be something like
# self.__dict__[parameter.name] = property(...) ?
col = Collection()
col.add(par1)
col.add(par2)
col['par1'] # gives the whole object
# Now here is what I would like to get:
# col.par1 -> should result like col['par1'].v
# col.par1 = 5 -> should result like col['par1'].v = 5
Other questions that I put to understand property():
Why do managed attributes just work for class attributes and not for instance attributes in python?
How can I assign a new class attribute via __dict__ in python?
Look at built-in functions getattr and setattr. You'll probably be a lot happier.
Using the same get/set functions for both classes forces you into an ugly hack with the argument list. Very sketchy, this is how I would do it:
In class SingleParameter, define get and set as usual:
def get(self):
return self._s
def set(self, value):
self._s = value
In class Collection, you cannot know the information until you create the property, so you define the metaset/metaget function and particularize them only later with a lambda function:
def metaget(self, par):
return par.s
def metaset(self, value, par):
par.s = value
def add(self, par):
self[par.name] = par
setattr(Collection, par.name,
property(
fget=lambda x : Collection.metaget(x, par),
fset=lambda x, y : Collection.metaset(x,y, par))
Properties are meant to dynamically evaluate attributes or to make them read-only. What you need is customizing attribute access. __getattr__ and __setattr__ do that really fine, and there's also __getattribute__ if __getattr__ is not enough.
See Python docs on customizing attribute access for details.
Have you looked at the traits package? It seems that you are reinventing the wheel here with your parameter classes. Traits also have additional features that might be useful for your type of application (incidently I know a person that happily uses traits in neural simulations).
Now I implemented a solution with set-/getattr:
class Collection(object):
...
def __setattr__(self, name, value):
if 'dict' in self.__dict__:
if name in self.dict:
self[name].v = value
else:
self.__dict__[name] = value
def __getattr__(self, name):
return self[name].v
There is one thing I quite don't like that much: The attributes are not in the __dict__. And if I have them there as well I would have a copy of the value - which can be dangerous...
Finally I succeded to implement the classes with property(). Thanks a lot for the advice. It took me quite a bit to work it out - but I can promise you that this exercise helps you to understand better pythons OOP.
I implemented it also with __getattr__ and __setattr__ but still don't know the advantages and disadvantages to the property-solution. But this seems to be worth another question. The property-solutions seems to be quit clean.
So here is the code:
class SingleParameter(object):
def __init__(self, name, default_value=0, unit='not specified'):
self.name = name
self.default_value = default_value
self.unit = unit
self.set(default_value)
def get(*args):
self = args[0]
print "get(): "
print args
return self._v
def set(*args):
print "set(): "
print args
self = args[0]
value = args[-1]
self._v = value
v = property(fget=get, fset=set, doc='value of parameter')
class Collection(dict):
# inheriting from dict saves the methods: __getitem__ and __init__
def add(self, par):
self[par.name] = par
# Now here comes the tricky part.
# (Note: this property call the get() and set() methods with one
# more argument than the property of SingleParameter)
setattr(Collection, par.name,
property(fget=par.get, fset=par.set))
# Applying the classes:
par1 = SingleParameter(name='par1', default_value=10, unit='mV')
par2 = SingleParameter(name='par2', default_value=20, unit='mA')
col = Collection()
col.add(par1)
col.add(par2)
# Setting parameter values:
par1.v = 13
col.par1 = 14
# Getting parameter values:
par1.v
col.par1
# checking identity:
par1.v is col.par1
# to access the whole object:
col['par1']
As I am new I am not sure how to move on:
how to treat follow up questions (like this itself):
get() is seems to be called twice - why?
oop-design: property vs. "__getattr__ & __setattr__" - when should I use what?
is it rude to check the own answer to the own question as accepted?
is it recommended to rename the title in order to put correlated questions or questions elaborated with the same example into the same context?
Other questions that I put to understand property():
Why do managed attributes just work for class attributes and not for instance attributes in python?
How can I assign a new class attribute via __dict__ in python?
I have a class that does something similar, but I did the following in the collection object:
setattr(self, par.name, par.v)

Categories