Using Python Factory Idioms - python

Below is a pattern from :https://python-3-patterns-idioms-test.readthedocs.io/en/latest/Factory.html . My question is this, is this still the best idiom/pattern to do generic object creation in Python 3.x? I can't seem to find much on this topic. The code is below:
class Shape(object):
# Create based on class name:
def factory(type):
#return eval(type + "()")
if type == "Circle": return Circle()
if type == "Square": return Square()
assert 0, "Bad shape creation: " + type
factory = staticmethod(factory)
class Circle(Shape):
def draw(self): print("Circle.draw")
def erase(self): print("Circle.erase")
class Square(Shape):
def draw(self): print("Square.draw")
def erase(self): print("Square.erase")
# Generate shape name strings:
def shapeNameGen(n):
types = Shape.__subclasses__()
for i in range(n):
yield random.choice(types).__name__
shapes = \
[ Shape.factory(i) for i in shapeNameGen(7)]
for shape in shapes:
shape.draw()
shape.erase()
You can also create a factory by using the __class__ method as well I've noticed, but I'm unsure of the best way to use this.

I could be missing something, but I don't like this pattern.
You already have factories for Circle and Square - Circle and Square. :)
The code in your question unnecessarily hardcodes the class names in factory and then goes through some extra hoops by getting the names of the subclasses of Shape and then calling factory with those names.
A more direct way to generate the shapes list is
types = Shape.__subclasses__()
shapes = [random.choice(types)() for _ in range(7)]
I have a situation where I could have multiple geometries being given. This is an over simplified example, but I am getting JSON response of various geometries, and instead of having to write multiple if statements multiple times, I thought using a Factory could reduce the issue: so Shape(JSON) -> Circle or Shape(JSON) - Square
This does not justify the factory as it is coded here. You could have a simple dictionary like
classes = {'Circle': Circle, 'Square': Square, ...}
or possibly create it dynamically with
classes = {cls.__name__:cls for cls in Shape.__subclasses__()}
and then call classes[some_string]() for the instantiation. You can even dynamically instantiate a class by string name using getattr.

Related

Python functions with dots

I have defined a of ordered pairs called f and defined a function applyfunction that goes through the ordered pairs looking at the first value to compare and when it does match to print the second value.
f = {(1,2),(2,4),(3,6),(4,8)}
def applyfunction (f,x):
for xy in f:
if xy[0]==x:
print(xy[1])
applyfunction(f,3)
The above works just the way I want it to. In the meantime I have seen that in python there are functions that have a dot notation and I think that would be useful here. So my question, how can I rewrite the applyfunction definition such that I can use the following notation: f.applyfunction(3)?
You can wrap the ordered pairs into a class of your own, which has the method (method == a function inside a class) you mentioned inside of it.
class OrderedPairWrapper():
def __init__(self, op):
self.op = op
def applyfunction (self, x):
for xy in self.op:
if xy[0]==x:
print(xy[1])
f = {(1,2),(2,4),(3,6),(4,8)}
f = OrderedPairWrapper(f)
print(f.applyfunction(3))
# 6
Dots are used to access methods of a class using its object name. If you want to access that using dot operator, create an object called f for a class with a method applyfunction. Then you can accomplish your desired task

How to create an overloaded constructor?

I have looked at What is a clean pythonic way to have multiple constructors in python, but I need further assistance since I'm still a newbie. The example there is when you have just one parameter, and I have four.
Let's say that I have a class:
class Word:
def __init__(self, wordorphrase, explanation, translation, example):
self.wordorphrase = wordorphrase
self.explanation = explanation
self.example = example
self.translation = translation
Now I can create Word objects only by passing four parameters when creating an object, for instance:
w = Word(self.get_word(), self.get_explanation(), self.get_translation(), self.get_example())
How should I modify my __init__ method so that I can create objects by:
w = Word()
One way that you could accomplish this is by specifying default arguments for your four variables that are not self. I tested the following code and was able to create instances of the word class without passing any arguments.
class Word:
def __init__(self, wordorphrase=None, explanation=None, translation=None, example=None):
self.wordorphrase = wordorphrase
self.explanation = explanation
self.example = example
self.translation = translation

Python - Recommended way to dynamically add methods within a class

I have a class where I want to initialize an attribute self.listN and an add_to_listN method for each element of a list, e.g. from attrs = ['list1', 'list2'] I want list1 and list2 to be initialized as empty lists and the methods add_to_list1 and add_to_list2 to be created. Each add_to_listN method should take two parameters, say value and unit, and append a tuple (value, unit) to the corresponding listN.
The class should therefore look like this in the end:
class Foo():
def __init__(self):
self.list1 = []
self.list1 = []
def add_to_list1(value, unit):
self.list1.append((value, unit))
def add_to_list2(value, unit):
self.list2.append((value, unit))
Leaving aside all the checks and the rest of the class, I came up with this:
class Foo():
def __init__(self):
for attr in ['list1', 'list2']:
setattr(self, attr, [])
setattr(self, 'add_to_%s' % attr, self._simple_add(attr))
def _simple_add(self, attr):
def method(value, unit=None):
getattr(self, attr).append((value, unit))
return method
I also checked other solutions such as the ones suggested here and I would like to do it "right", so my questions are:
Are/Should these methods (be) actually classmethods or not?
Is there a cost in creating the methods in __init__, and in this case is there an alternative?
Where is the best place to run the for loop and add these methods? Within the class definition? Out of it?
Is the use of metaclasses recommended in this case?
Update
Although Benjamin Hodgson makes some good points, I'm not asking for a (perhaps better) alternative way to do this but for the best way to use the tools that I mentioned. I'm using a simplified example in order not to focus on the details.
To further clarify my questions: the add_to_listN methods are meant to be additional, not to replace setters/getters (so I still want to be able to do l1 = f.list1 and f.list1 = [] with f = Foo()).
You are making a design error. You could override __getattr__, parse the attribute name, and return a closure which does what you want, but it's strange to dynamically generate methods, and strange code is bad code. There are often situations where you need to do it, but this is not one of them.
Instead of generating n methods which each do the same thing to one of n objects, why not just write one method which is parameterised by n? Something roughly like this:
class Foo:
def __init__(self):
self.lists = [
[],
[]
]
def add(self, row, value):
self.lists[row].append(value)
Then foo.add1(x) becomes simply foo.add(1, x); foo.add2(x) becomes foo.add(2, x), and so on. There's one method, parameterised along the axis of variation, which serves all cases - rather than a litany of ad-hoc generated methods. It's much simpler.
Don't mix up the data in your system with the names of the data in your system.

global methods or functions in python

I am making a small game in Python using pythonista on my ipad.
I made a vector class that contains the coordinates and several functions to add, get a length, set a length. I have another class called Game in which I have my game variables and functions. I can define a vector lets say
self.pos=vector(200,200)
But if I want to work out the length, I can't call the getlength function because I'm not in the right class.
Example (I've taken out most of the code):
class vector(objet):
def __init(self,x,y):
self.x=x
self.y=y
def getlength(self):
return sqrt(self.x**2+self.y**2)
def addvec(self,a,b):
return vector(a.x+b.x,a.y,b.y)
class Game(object):
def __init__(self):
self.pos=vector(200,200)
self.pos=vector(200,200)
def loop(self):
## here i want something like d= length of self.pos !!
class MyScene(Scene):
def setup(self):
self.game=Game()
def draw(self):
self.game.loop()
run(MyScene())
Thanks,
Nicolas
EDIT : the call
sum=addvec(self.pos,self.pos2)
obviously doesn't work because self is a Game class. How can I do it?
Why do you use two arguments for the getLength function? The second one is a vector (I assume) so it would be better to use:
def getLength(self):
return sqrt(self.x**2+self.y**2)
and then just call:
d = self.pos.getLength()
If you would want to add two vectors together you would do something like this:
def add(self,other_vector):
return vector(self.x+other_vector.x,self.y+other_vector.y)
so you would call:
sum = self.pos.add(some_other_vector)
BTW: Classes should always be written in CamelCase. And maybe you should read something about object oriented programming in python: http://code.tutsplus.com/articles/python-from-scratch-object-oriented-programming--net-21476

Cast base class to derived class python (or more pythonic way of extending classes)

I need to extend the Networkx python package and add a few methods to the Graph class for my particular need
The way I thought about doing this is simplying deriving a new class say NewGraph, and adding the required methods.
However there are several other functions in networkx which create and return Graph objects (e.g. generate a random graph). I now need to turn these Graph objects into NewGraph objects so that I can use my new methods.
What is the best way of doing this? Or should I be tackling the problem in a completely different manner?
If you are just adding behavior, and not depending on additional instance values, you can assign to the object's __class__:
from math import pi
class Circle(object):
def __init__(self, radius):
self.radius = radius
def area(self):
return pi * self.radius**2
class CirclePlus(Circle):
def diameter(self):
return self.radius*2
def circumference(self):
return self.radius*2*pi
c = Circle(10)
print c.radius
print c.area()
print repr(c)
c.__class__ = CirclePlus
print c.diameter()
print c.circumference()
print repr(c)
Prints:
10
314.159265359
<__main__.Circle object at 0x00A0E270>
20
62.8318530718
<__main__.CirclePlus object at 0x00A0E270>
This is as close to a "cast" as you can get in Python, and like casting in C, it is not to be done without giving the matter some thought. I've posted a fairly limited example, but if you can stay within the constraints (just add behavior, no new instance vars), then this might help address your problem.
Here's how to "magically" replace a class in a module with a custom-made subclass without touching the module. It's only a few extra lines from a normal subclassing procedure, and therefore gives you (almost) all the power and flexibility of subclassing as a bonus. For instance this allows you to add new attributes, if you wish.
import networkx as nx
class NewGraph(nx.Graph):
def __getattribute__(self, attr):
"This is just to show off, not needed"
print "getattribute %s" % (attr,)
return nx.Graph.__getattribute__(self, attr)
def __setattr__(self, attr, value):
"More showing off."
print " setattr %s = %r" % (attr, value)
return nx.Graph.__setattr__(self, attr, value)
def plot(self):
"A convenience method"
import matplotlib.pyplot as plt
nx.draw(self)
plt.show()
So far this is exactly like normal subclassing. Now we need to hook this subclass into the networkx module so that all instantiation of nx.Graph results in a NewGraph object instead. Here's what normally happens when you instantiate an nx.Graph object with nx.Graph()
1. nx.Graph.__new__(nx.Graph) is called
2. If the returned object is a subclass of nx.Graph,
__init__ is called on the object
3. The object is returned as the instance
We will replace nx.Graph.__new__ and make it return NewGraph instead. In it, we call the __new__ method of object instead of the __new__ method of NewGraph, because the latter is just another way of calling the method we're replacing, and would therefore result in endless recursion.
def __new__(cls):
if cls == nx.Graph:
return object.__new__(NewGraph)
return object.__new__(cls)
# We substitute the __new__ method of the nx.Graph class
# with our own.
nx.Graph.__new__ = staticmethod(__new__)
# Test if it works
graph = nx.generators.random_graphs.fast_gnp_random_graph(7, 0.6)
graph.plot()
In most cases this is all you need to know, but there is one gotcha. Our overriding of the __new__ method only affects nx.Graph, not its subclasses. For example, if you call nx.gn_graph, which returns an instance of nx.DiGraph, it will have none of our fancy extensions. You need to subclass each of the subclasses of nx.Graph that you wish to work with and add your required methods and attributes. Using mix-ins may make it easier to consistently extend the subclasses while obeying the DRY principle.
Though this example may seem straightforward enough, this method of hooking into a module is hard to generalize in a way that covers all the little problems that may crop up. I believe it's easier to just tailor it to the problem at hand. For instance, if the class you're hooking into defines its own custom __new__ method, you need to store it before replacing it, and call this method instead of object.__new__.
I expanded what PaulMcG did and made it a factory pattern.
class A:
def __init__(self, variable):
self.a = 10
self.a_variable = variable
def do_something(self):
print("do something A")
class B(A):
def __init__(self, variable=None):
super().__init__(variable)
self.b = 15
#classmethod
def from_A(cls, a: A):
# Create new b_obj
b_obj = cls()
# Copy all values of A to B
# It does not have any problem since they have common template
for key, value in a.__dict__.items():
b_obj.__dict__[key] = value
return b_obj
if __name__ == "__main__":
a = A(variable="something")
b = B.from_A(a=a)
print(a.__dict__)
print(b.__dict__)
b.do_something()
print(type(b))
Result:
{'a': 10, 'a_variable': 'something'}
{'a': 10, 'a_variable': 'something', 'b': 15}
do something A
<class '__main__.B'>
You could simply create a new NewGraph derived from Graph object and have the __init__ function include something like self.__dict__.update(vars(incoming_graph)) as the first line, before you define your own properties. In this way you basically copy all the properties from the Graph you have onto a new object, derived from Graph, but with your special sauce.
class NewGraph(Graph):
def __init__(self, incoming_graph):
self.__dict__.update(vars(incoming_graph))
# rest of my __init__ code, including properties and such
Usage:
graph = function_that_returns_graph()
new_graph = NewGraph(graph)
cool_result = function_that_takes_new_graph(new_graph)
I encountered the same question when contributing to networkx, because I need many new methods for Graph. The answer by #Aric is the simplest solution, but inheritance is not used. Here a native networkx feature is utilise, and it should be more efficient.
There is a section in networkx tutorial, using the graph constructors, showing how to init Graph object from existing objects for a graph, especially, another graph object. This is the example shown there, you can init a new DiGraph object, H, out of an existing Graph object, G:
>>> G = Graph()
>>> G.add_edge(1, 2)
>>> H = nx.DiGraph(G) # create a DiGraph using the connections from G
>>> list(H.edges())
[(1, 2), (2, 1)]
Note the mathematical meaning when converting an existing graph to a directed graph. You can probably realise this feature via some function or constructor, but I see it as an important feature in networkx. Haven't checked their implementation, but I guess it's more efficient.
To preserve this feature in NewGraph class, you should make it able to take an existing object as argument in __init__, for example:
from typing import Optional
import networkx as nx
class NewGraph(nx.Graph):
def __init__(self, g: Optional[nx.Graph] = None):
"""Init an empty directed graph or from an existing graph.
Args:
g: an existing graph.
"""
if not g:
super().__init__()
else:
super().__init__(g)
Then whenever you have a Graph object, you can init (NOT turn it directly to) a NewGraph object by:
>>> G = nx.some_function()
...
>>> NG = NewGraph(G)
or you can init an empty NewGraph object:
>>> NG_2 = NewGraph()
For the same reason, you can init another Graph object out of NG:
>>> G_2 = nx.Graph(NG)
Most likely, there are many operations after super().__init__() when initiating a NewGraph object, so the answer by #PaulMcG, as he/she mentioned, is not a good idea in such circumstance.
If a function is creating Graph objects, you can't turn them into NewGraph objects.
Another option is for NewGraph is to have a Graph rather than being a Graph. You delegate the Graph methods to the Graph object you have, and you can wrap any Graph object into a new NewGraph object:
class NewGraph:
def __init__(self, graph):
self.graph = graph
def some_graph_method(self, *args, **kwargs):
return self.graph.some_graph_method(*args, **kwargs)
#.. do this for the other Graph methods you need
def my_newgraph_method(self):
....
For your simple case you could also write your subclass __init__ like this and assign the pointers from the Graph data structures to your subclass data.
from networkx import Graph
class MyGraph(Graph):
def __init__(self, graph=None, **attr):
if graph is not None:
self.graph = graph.graph # graph attributes
self.node = graph.node # node attributes
self.adj = graph.adj # adjacency dict
else:
self.graph = {} # empty graph attr dict
self.node = {} # empty node attr dict
self.adj = {} # empty adjacency dict
self.edge = self.adj # alias
self.graph.update(attr) # update any command line attributes
if __name__=='__main__':
import networkx as nx
R=nx.gnp_random_graph(10,0.4)
G=MyGraph(R)
You could also use copy() or deepcopy() in the assignments but if you are doing that you might as well use
G=MyGraph()
G.add_nodes_from(R)
G.add_edges_from(R.edges())
to load your graph data.
The __class__ assignment approach actually alters the variable. If you only want to call a function form the super class you can use super. For example:
class A:
def __init__(self):
pass
def f(self):
print("A")
class B(A):
def __init__(self):
super().__init__()
def f(self):
print("B")
b = B()
b.f()
super(type(b), b).f()
is returning
B
A
Have you guys tried
[Python] cast base class to derived class
I have tested it, and seems it works. Also I think this method is bit better than below one since below one does not execute init function of derived function.
c.__class__ = CirclePlus

Categories