Initializing instance variable: Idiomatic way - python

In Python what is the idiomatic way for initializing Python's instance variable:
class Test:
def __init__(self, a, b, c, d):
self.a = a
self.b = b
self.c = c
self.d = d
Or
class Test2:
def __init__(self, data):
self.a = data[0]
self.b = data[1]
self.c = data[2]
self.d = data[3]
UPDATE: I have around 20 instance variables for a class named Link:
self.street
self.anode
self.bnode
self.length
self.setbackA
self.setbackB
self.bearingA
self.bearingB
self.ltype
self.lanesAB
self.leftAB
self.rightAB
self.speedAB
self.fspdAB
self.capacityAB
self.lanesBA
self.leftBA
self.rightBA
self.speedBA
self.fspdBA
self.capacityBA
self.use
Each variable is related to the class Link. Is there a recommended way of refactoring this?

The former, since it's more explicit about what the parameters are and what the object requires.
If you do need to pass in your data as a tuple, there's a shortcut you can use. Instead of doing the latter, or something like:
test = Test(data[0], data[1], data[2], data[3])
You can instead unpack the list/tuple and do:
test = Test(*data)
If you need to pass in a bunch of data (more then 4-5), you should look into either using optional/keyword arguments, creating a custom object to hold some of the data, or using a dictionary:
config = Config(a, b, c, d)
test = Test(e, f, config, foo=13, bar=True)
I would probably refactor your Link class to look like this:
class Node(object):
def __init__(self, node, setback, bearing):
self.node = node
self.setback = setback
self.bearing = bearing
class Connection(object):
def __init__(self, lanes, left, right, speed, fspd, capacity):
self.lanes = lanes
self.left = left
self.right = right
self.speed = speed
self.fspd = fspd
self.capacity = capacity
class Link(object):
def __init__(self, street, length, ltype, use, a, b, ab, ba):
self.street = street
self.length = length
self.ltype = ltype
self.use = use
self.a = a
self.b = b
self.ab = ab
self.ba = ba
I saw that you had some duplicate data, so pulled those off into a separate object. While this doesn't reduce on the number of fields you have, overall, it does make the parameters you need to pass in smaller.
Having a large number of fields isn't bad, but having a large number of parameters generally is. If you can write your methods in a way that they don't need a huge amount of parameters by bundling together data, then it doesn't really matter how many fields you have.

Unpacking an array into a bunch of named variables suggests you should have started with named variables in the first place - stick with the first one.
There is only one reason you might want the second one here - you have something that is inconsiderately producing lists rather than objects. If that does happen:
data = get_data_in_list_form()
actual_data = Test(*data)
Can you group some of your data:
self.street
self.ltype
self.use
self.length
# .a and .b can be instances of NodeConnection
self.a.setback
self.a.bearing
self.b.setback
self.b.bearing
self.b.node
# .ab and .ba can be a separate class, "UniDirectionalLink
self.ab.lanes
self.ab.left
self.ab.right
self.ab.speed
self.ab.fspd
self.ab.capacity
self.ba.lanes
self.ba.left
self.ba.right
self.ba.speed
self.ba.fspd
self.ba.capacity
There's no need to do everything in a constructor here:
link = (
Link(street=..., ltype=..., use=..., length=...)
.starting_at(node_a, bearing=..., setback=...)
.finishing_at(node_b, bearing=..., setback=...)
.forward_route(lanes, left, right, speed, fspd, capacity)
.reverse_route(lanes, left, right, speed, fspd, capacity)
)

Related

Assigning objects as attributes to each other

I have two classes of objects, Top and Bottom. I create the objects in pairs and want to assign them to each other so that I can reference the other's corresponding object in my project. Is there a more elegant way to do this than my sample code?
class Top:
def __init__(self,bottom):
self.bottom = bottom
class Bottom:
def __init__(self):
self.top = None
def set_top(self,top):
self.top = top
Top_Bottom_List = []
for i in range(5):
Current_Bottom = Bottom()
Current_Top = Top(Current_Bottom)
Current_Bottom.set_top(Current_Top)
Top_Bottom_list.append((Current_Top,Current_Bottom))
My problem is that I'd like to create an instance of two classes at the same time, but obviously one of them will have to be defined first or the other one won't have anything to set a value to.
I'd prefer a solution that doesn't give one class a "preferred" status over the other.
class Top:
def __init__(self, bottom=None):
self.bottom = bottom
class Bottom:
def __init__(self, top=None):
self.top = top
def link(t, b):
t.bottom = b
b.top = t
return t, b
top_bottom_list = [link(Top(), Bottom()) for _ in range(5)]
If you can't immediately pass the linked object to each other on instantiation, do nothing initially, then let an additional function handle pairing the two together after the fact. An additional function can take care of the instantiation as well
def make_pair():
t = Top()
b = Bottom()
return link(t, b)
top_bottom_list = [make_pair() for _ in range(5)]
Nothing that can’t be solved by another layer of indirection:
def create_pair():
b = Bottom()
t = Top(b)
b.top = t
return b, t
…
top_bottom_list = [create_pair() for _ in range(5)]
If the coupling is 1:1, you could also change the constructors:
class Top:
def __init__(self, bottom = None):
if bottom is not None:
bottom.top = self
self.bottom = bottom
class Bottom:
def __init__(self, top = None):
if top is not None:
top.bottom = self
self.top = top
And use it as:
def create_pair():
b = Bottom(Top())
return b, b.top
… though I’m not sure I like this code.

Evaluating tree without having to build an interpreter (Python)

Earlier this week I asked a generic question in a related SO community regarding constructing mathematical trees using OOP. The main takeaway was that the Composite and Interpreter patterns were the go-to patterns for this kind of application.
I then spent several days looking around online for resources on how these are constructed. I'm still convinced that I do not need to construct an entire interpreter and that a composite might be sufficient for my purposes.
From the other question I was trying to construct this tree:
Without using OOP, I'd probably do something like this:
import numpy as np
def root(B, A):
return B+A
def A(x,y,z):
return x*np.log(y)+y**z
def B(alpha, y):
return alpha*y
def alpha(x,y,w):
return x*y+w
if __name__=='__main__':
x,y,z,w = 1,2,3,4
result = root(B(alpha(x,y,w),y), A(x,y,z))
This would give a correct result of 20.693147180559947. I tried to use the composite pattern to do something similar:
class ChildElement:
'''Class representing objects at the bottom of the hierarchy tree.'''
def __init__(self, value):
self.value = value
def __repr__(self):
return "class ChildElement with value"+str(self.value)
def component_method(self):
return self.value
class CompositeElement:
'''Class representing objects at any level of the hierarchy tree except for the bottom level.
Maintains the child objects by adding and removing them from the tree structure.'''
def __init__(self, func):
self.func = func
self.children = []
def __repr__(self):
return "class Composite element"
def append_child(self, child):
'''Adds the supplied child element to the list of children elements "children".'''
self.children.append(child)
def remove_child(self, child):
'''Removes the supplied child element from the list of children elements "children".'''
self.children.remove(child)
def component_method(self):
'''WHAT TO INCLUDE HERE?'''
if __name__=='__main__':
import numpy as np
def e_func(A, B):
return A+B
def A_func(x,y,z):
return x*np.log(y)+y**z
def B_func(alpha,y):
return alpha*y
def alpha_func(x,y,w):
return x*y+w
x = ChildElement(1)
y = ChildElement(2)
z = ChildElement(3)
w = ChildElement(4)
e = CompositeElement(e_func)
A = CompositeElement(A_func)
B = CompositeElement(B_func)
alpha = CompositeElement(alpha_func)
e.children = [A, B]
A.children = [x, y, z]
B.children = [alpha, y]
alpha.children = [x, y, w]
e.component_method()
I got stuck in the last line, however. It seems that if I call the component_method at the level of composite class instance e, it will not work, since the architecture is not built to handle adding two Child or Composite objects.
How can I get this to work? What should the component_method for my CompositeElement class contain?
def component_method(self):
values = [child.component_method() for child in self.children]
return self.func(*values)
This will evaluate the child nodes and pass the values to the function of the node itself, returning the value.

Class method uses an array not passed

So, I'm tring to program a variant of the knapsack problem but I'm really new to the use of Python and I stumbled on this.
I'm using Jupyter (Python 3)
class Gene(object):
def __init__(self, weight, price):
self.weight = weight
self.price = price
obj1 = Gene(10, 20)
obj2 = Gene(25, 5)
obj3 = Gene(5, 10)
genes = [obj1, obj2, obj3]
class Chromosomes(object):
def __init__(self, flagIndex_of_items_contained = []):
self.flagIndex_of_items_contained = flagIndex_of_items_contained
self.myWeight = self.Define_myWeight()
def Define_myWeight(self):
weight = 0
for index_flag in range(len(self.flagIndex_of_items_contained)):
if(self.flagIndex_of_items_contained[index_flag] == 1):
weight = weight + genes[index_flag].weight
return weight
chromosome1 = Chromosomes([1,0,1])
print("chromosome1 weight: ", chromosome1.myWeight)
Output
chromosome1 weight: 15
BUT
genes[index_flag].weight
How can this command work if i don't pass the array genes to the class?
The problem is that your variable genes lives on the same level as the classes, the module level. In the problematic line
weight = weight + genes[index_flag].weight
the interpreter simply sees that there is no local variable with the scope of the function Define_myWeight, hence it checks the global scope (module level). On this level genes exists and the interpreter can use it.
Besides that, issues because of which your code has been considered "badly written".
Use global variables only if they are absolutely necessary. Check some arbitrary tutorial on globals to understand this proposition.
You should never use a mutable object as default parameter. Lists are mutable objects in Python, that means they can be changed. Use immutable objects, like tuples, in such cases.
def func1(some_arg = []): # bad
def func1(some_arg = ()): # ok
Do not mix different format styles. Use either CamelCase or names_with_underscores. Check out the Python Style Guide for that.
Here is an idea to improve your code. Chromosomes are made up of different genes. The following code models this relation.
class Gene:
def __init__(self, weight, price):
self.weight = weight
self.price = price
class Chromosom:
def __init__(self):
self.genes = []
self.flag_idx = []
self.weight = 0
def add_gene(self, weight, price):
self.genes.append(Gene(weight, price))
def compute_weight(self, flags):
for i, flag in enumerate(flags):
if flag == 1:
self.weight += self.genes[i].weight
Usage:
ch = Chromosom()
ch.add_gene(10, 20)
ch.add_gene(25, 5)
ch.add_gene(5, 10)
ch.compute_weight((1, 0, 1))
print(ch.weight)

Python classes and types

I think I'm misusing the concept of subclass. I'm working on a hobby project with Grids and Cells.
What I have, is the implementation of a Cell class, and its subclass HexCell which basically redefines many of the attributes/methods like so:
class Cell:
def __init__(self, row_loc, col_loc):
self.row = row_loc
self.col = col_loc
self.links = set()
self.neighbors = 4*[None]
def __repr__(self):
return f'Cell #({self.row},{self.col})'
def link(self, other, bidir = True):
self.links.add(other)
if bidir: other.links.add(self)
Then I have a subclass that is the HexGrid which follows a similar structure with new parameters.
class HexCell(Cell):
def __init__(self, r_out, th_around):
# I'm indexing Hex cells around a center cell
# instead of by rows and columns; Prefixed hex
# as they follow the hexagon, and not regular polar coordinates.
self.hex_r = r_out
self.hex_th = th_around
self.neighbors = 6*[None]
self.links = set()
def __repr__(self):
return f"HexCell #[{self.hex_r}, {self.hex_th}]"
def bind(self, other, to_dir):
to_dir = to_dir % 6
if (self.neighbors[to_dir] is None):
self.neighbors[to_dir] = other
other.neighbors[to_dir - 3] = self
# Hexagonal grids share neighbors.
other_1 = other.neighbors[to_dir - 2]
if (self.neighbors[to_dir - 1] is None) & (other_1 is not None):
self.bind(other_1, to_dir - 1)
other_5 = other.neighbors[to_dir - 4]
if (self.neighbors[to_dir - 5] is None) & (other_5 is not None):
self.bind(other_5, to_dir - 5)
In this case, the method self.link(other) is shared, but other attributes change from rectangular grid to hexagonal like the locaion from (row, col) to (hex_r, hex_th), or neighbors as a 4-list or 6-list. Thus I'd like these attributes to be dependent on a another cell-type attribute and transferred down to the subclass.
Correct use of subclassing needs to obey the following substitution principle:
If there are some objects x_1 of type T_1 and x_2 of type T_2 such that issubclass(T_2, T_1) == True, then any property that applies to x_1 must also apply for x_2.
In other words, you expect subclassing to implement new behaviours, not to change existing behaviours.
In you example, the change of coordinate system itself is a change of behaviour and thus HexCell should not inherit from Cell.
What you can do is create a base class BaseCell that encapsulates the common behaviour between Cell and HexCell and inherit from it.
class BaseCell:
def __init__(self):
self.links = set()
self.neighbors = []
def add_neighbor(self, other):
self.neighbors.append(other)
def link(self, other, bidirectional=True):
self.links.add(other)
if bidirectional:
other.link(self, bidirectional=False)
class Cell(BaseCell):
def __init__(self, row_loc, col_loc):
self.row = row_loc
self.col = col_loc
super().__init__()
def __repr__(self):
return f'Cell #({self.row},{self.col})'
class HexCell(Cell):
def __init__(self, r_out, th_around):
self.hex_r = r_out
self.hex_th = th_around
super().__init__()
def __repr__(self):
return f"HexCell #[{self.hex_r}, {self.hex_th}]"
def bind(self, other, to_dir):
...
Your Cell class is in fact not an abstract "Cell", but a square cell in two-dimensional space (has exactly 4 neighbours, has "row" and "col" position). Such cell may not be subclassed by a hex cell, because hex cell is just a different type of cell : )
As you noticed, the only common things are link() method and links attribute. If you insist on subclassing, you could create something like:
class LinkedObject():
def __init__(self):
self.links = set()
def link(self, other, bidir = True):
self.links.add(other)
if bidir: other.links.add(self)
class SquareCell(LinkedObject):
# "Cell" class here
class HexCell(LinkedObject):
# HexCell here

How can I recursively check if nested subclasses have each other as parent?

I know my question is somewhat confusing, but I'll try my best to make myself clear. Here's a sample code I've made to try to illustrate the problem:
class X:
def __init__(self, p=None):
self.parent = p
class Y(X):
def __init__(self, p=None):
X.__init__(self, p)
def recursive_check(self):
if (self.parent.parent.parent):
print(self.parent.parent.parent)
x0 = X()
x1 = X(x0)
x2 = X(x1)
y = Y(x2)
y.recursive_check()
As you can see, if you try to access self.parent.parent.parent.parent you'll get a None. The problem is that I don't know how nested the parents can be, so I can't directly test if they exist like that. I want to check recursively the depth of the nest, it could have 100 instances nesting on each other. For the sake of this example, I just need to print each parent address if it exists.
EDIT:
Considering the accepted answer, that's how I've achieved what I wanted:
class X:
def __init__(self, p=None):
self.parent = p
def recursive_check(self):
if self.parent:
print(self.parent)
self.parent.recursive_check()
class Y(X):
def __init__(self, p=None):
X.__init__(self, p)
x0 = X()
x1 = X(x0)
x2 = X(x1)
y = Y(x2)
y.recursive_check()
OBS: I'm developing a GUI for Pygame and this has to do with nested Panels (Layout Management). I don't know how nested the panels can be.
The following will do the memory address printing:
def recursive_check(self):
if self.parent:
print(id(self.parent))
self.parent.recursive_check()
If your ancestry can have circles, you'd have to collect each parent and pass that set on down the recursive calls to avoid infinite recursion.
Just use a loop, which avoids runtime errors when the stack of parents gets too large.
def recursive_check(self):
top = self
p = self.parent
while p is not None:
top = p
p = top.parent
print(top)

Categories

Resources