Does this design represent circular dependency? - python

Imagine this example (that may be run without any errors):
from random import uniform
class Car:
def __init__(self, position):
self.position = position
self.sensor = Sensor(id, self)
class Sensor:
def __init__(self, id, carrier_car):
self.id = id
self.position = self.compute_position(carrier_car)
def compute_position(self, carrier_car):
return [carrier_car.position[0] + uniform(1,3), carrier_car.position[0] + uniform(2,4)]
car1 = Car([10,10])
print("This car's sensor coordinate is ({},{}).".format(car1.sensor.position[0], car1.sensor.position[1]))
Question 1.
Given the line self.sensor = Sensor(id, self), I don't understand how this line runs without any problem. Isn't this circular dependency? (self, in the argument, is an object of Car which has not yet been completely created (initialized) at the point it is passed to the initializer of Sensor.)
Question 2.
If this a circular dependency, how can I resolve it?

The self object that is passed to Sensor(id, self) is an already existing Car instance, but it does not have a sensor attribute yet.
Since Sensor only requires the position attribute of the carrier_car object, this is not a problem, since it has already been created in the line self.position = position.
In any case, this design is a bit confusing. I would not pass a Car object to Sensor, but just a position, as this is all that Sensor needs to know about the car. This would also simplify the code:
from random import uniform
class Car:
def __init__(self, position):
self.position = position
self.sensor = Sensor(id, position)
class Sensor:
def __init__(self, id, position):
self.id = id
self.position = self.compute_position(position)
def compute_position(self, position):
return [position[0] + uniform(1,3), position[0] + uniform(2,4)]
car1 = Car([10,10])
print("This car's sensor coordinate is ({},{}).".format(car1.sensor.position[0], car1.sensor.position[1]))

Related

Split big object into smaller sub objects in instance

What is the pythonic way to handle large objects? In my example I could have one big class creating one instance with many attributes or I could group some of them together (See class Car and class Motor):
class Car(object):
color = "red"
def __init__(self, num_wheels):
self.burst = Motor(self)
self.wheels = num_wheels
for i in range(self.wheels):
setattr(self, "wheel{}".format(i), Wheel(self, i))
def _query(self, value):
print "Get Serial Data: {}".format(value)
class Motor(object): # Object is loaded only once in Car instance
def __init__(self, top):
self._top = top
def temperature(self):
return self._top._query("GET MOTOR TEMP")
def rpm(self):
return self._top._query("GET MOTOR RPM")
class Wheel(object): # Object could be loaded many times in Car instance
def __init__(self, top, number):
self._top = top
self._number = number
def temperature(self):
return self._top._query("GET WHEEL TEMP {}".format(self._number))
def rpm(self):
return self._top._query("GET WHEEL RPM {}".format(self._number))
I think this even makes more sense, when the Car has more than one wheel, as I could add more wheels.
But since Motor is never used more than once and never used else where, is it better style to put them into the Car class:
class Car(object):
color = "red"
def __init__(self, num_wheels):
# Add wheels or other stuff
def _query(self, value):
print "Get Serial Data: {}".format(value)
def motor_temperature(self):
return self._query("GET MOTOR TEMP")
def motor_rpm(self):
return self.._query("GET MOTOR RPM")
I will have to access Car._query() from the Wheel and/or Motor class and my real life object contains about 40 attributes and methods I could group in 4-5 sub instances. Couldn't find much on this topic on the web.
If you use large numbers of instances, you can used slots to reduce memory footprint.

Python–Object AttributeError when accessing class attribute python

I have three classes: Item, Weapon, and BrassSword
When I try to access one of BrassSword's attributes ex.(name,image,etc.) It says, AttributeError: class BrassSword has no attribute 'image'
Here's the code:
import pygame, math, random
class Item(object):
def __init__(self,name,image,reuseable,value):
self.image=pygame.image.load(image)
self.itemattrs = ['name','image','reuseable','value']
self.path = image
self.name = name
self.x=0
self.y=0
self.reusable = reuseable
self.value = value
self.rect = [self.x,self.y,self.image.get_size()[0],self.image.get_size()[1]]
def onUse(self):
pass
def onThrow(self):
pass
class Weapon(Item):
def __init__(self,name,image,value,damage,maxdamage,speed):
super(Weapon,self).__init__('Weapon',image,True,value)
self.itemattrs = ['name','image','damage','maxdamage','value','speed']
self.damage=damage
self.maxdamage=maxdamage
self.speed = speed # Cooldown in frames
self.cooldown = 0
def onUpdate(self):
self.cooldown -= 1
def onUse(self,targetEntity):
if self.cooldown > 0:
return
self.cooldown = speed
targetEntity.hp-=random.range(damage,maxdamage)
if targetEntity.hp <= 0:
targetEntity.onDie()
def onThrow(self):
pass # TODO: Add throwing weapons
class BrassSword(Weapon):
def __init__(self):
super(BrassSword,self).__init__('item.weapon.brass_sword','testlevel/Ball.png',True,value,3,10,12)
You didn't post the code that's actually causing the error - namely where you access the attribute. However, you can't access an instance attribute by referring to the class - they are stored in a separate __dict__. Your superclass sets these attributes when it is instantiated in __init__(), as a property of self. After this, they can only be accessed through that self instance.
If you are trying to access the attribute similarly to this:
a = BrassSword.image
instead, you want to access it something like this:
sword = BrassSword()
a = sword.image
or:
sword = BrassSword().image
If you want to have a single image shared across all BrassSword instances, you need to declare it a class attribute like this:
class BrassSword(Weapon):
image = 'path/to/image'
def __init__(...):
...

Python 2.7, defining a base class with attributes, id with init constructor

I am trying to define a generic base class Geometry, with a unique id for each object starting at 0. I am using init as the method.
I am trying to create a generic base class named Geometry that I will use to organize geometry objects like point or polygon and containing an id attribute starting at 0. I know all of the objects should have a unique ID. I'm using the constructor (__init__) when creating a new Geometry object (integer). And would like for the base class to automatically assign the ID of the Geometry object.
Current code:
class Geometry(object):
def__init__(self,id):
self.id = id
I think I am on the right path but I am not positive. Should I have id = 0 above def__init__(self,id)?
Any guidance will be appreciated.
If the first line of your class is id = 0 then it becomes a class attribute and is shared by all instances of Geometry and all of its children.
Here is an example of using a class scoped variable:
#!/usr/bin/env python2
class Geometry(object):
# ident is a class scoped variable, better known as Geometry.ident
ident = 0
def __init__(self):
self.ident = Geometry.ident
Geometry.ident += 1
class Circle(Geometry):
def __init__(self, radius):
Geometry.__init__(self)
self.radius = radius
def __str__(self):
return '<Circle ident={}, {}>'.format(self.ident, self.radius)
class Equilateral(Geometry):
def __init__(self, sides, length):
# super is another way to call Geometry.__init__() without
# needing the name of the parent class
super(Equilateral, self).__init__()
self.sides = sides
self.length = length
def __str__(self):
return '<Equilateral ident={}, {}, {}>'.format(self.ident,
self.sides, self.length)
# test that ident gets incremented between calls to Geometry.__init__()
c = Circle(12)
e = Equilateral(3, 8)
f = Circle(11)
print c
assert c.ident == 0
print e
assert e.ident == 1
print f
assert f.ident == 2
Something feels wrong about this, though I've not put my finger on it.
class Geometry(object):
def __init__(self,id=0):
self.id = id
__init__ in python is invoked when you create an instance of that class
circle = Geometry(1)

Replacing member objects with subclasses in Python

I have the following problem that I will attempt to illustrate with the following example.
class Brick():
def __init__(self):
self.weight = 1
class House():
def __init__(self, number_bricks):
self.bricks = [Brick() for i in range(number_bricks)]
def get_weight(self):
return reduce(lambda x,y: x+y, [brick.weight for brick in self.bricks])
But now suppose I create a new kind of Brick, StrongBrick, so that I make a house, a subclass StrongHouse, where StrongBrick plays exactly the same role in StrongHouse as Brick plays in House. How can I do this in a nice way (not just retyping all the class definitions)?
So the basic idea is, how can I change a class which is composed of some objects to the same class but composed of say a subclass of the original member objects?
Thanks very much for any help you can give me.
You could have a factory (a brickyard?) and pass that to House.__init__().
class Brick(object): pass
class StrongBrick(Brick): pass
class House(object):
def __init__(self, brick_factory, num_bricks):
self.bricks = [brick_factory() for i in range(num_bricks)]
house = House(Brick, 10000)
strong_house = House(StrongBrick, 10000)
As you can see, subclassing House isn't even necessary to be able to construct houses from different types of bricks.
There are various ways to do this. You could make the relevant Brick class an attribute of the House class:
class House(object):
brick_class = Brick
def __init__(self, number_bricks):
self.bricks = [self.brick_class() for i in range(number_bricks)]
class StrongHouse(House):
brick_class = StrongBrick
Or, you could pass in the Brick class you want to use when constructing the House:
class House(object):
def __init__(self, brick_class, number_bricks):
self.bricks = [brick_class() for i in range(number_bricks)]
One nice pattern could be this:
class Brick(object):
weight = 1
class StrongBrick(Brick):
weight = 42
class House(object):
brick_type = Brick
def __init__(self, number_bricks):
self.bricks = [self.brick_type() for i in range(number_bricks)]
def get_weight(self):
return reduce(lambda x, y: x + y, [brick.weight for brick in self.bricks])
class StrongHouse(House):
brick_type = StrongBrick
Another is to make a function making a factory, and using an argument for the brick_type with default value:
class House(object):
def __init__(self, number_bricks, brick_type=Brick):
self.bricks = [brick_type() for i in range(number_bricks)]
def get_weight(self):
return reduce(lambda x, y: x + y, [brick.weight for brick in self.bricks])
def make_house_factory(brick_type):
def factory(number_bricks):
return House(number_bricks, brick_type)
return factory
StrongHouse = make_house_factory(StrongBrick)
Of course all such objects would be instances of the House only, even though I named StrongHouse here so that it resembles a class name.
But now suppose I create a new kind of Brick, StrongBrick, so that I make a house, a subclass StrongHouse, where StrongBrick plays exactly the same role in StrongHouse as Brick plays in House. How can I do this in a nice way (not just retyping all the class definitions)?
As all of the other answers have explained, you really don't want to create this parallel hierarchy at all. But to answer your direct question: You can create classes dynamically, so you can create a parallel hierarchy without copying and pasting all the class definitions. Classes are, after all, first-class objects.
Again, let me stress that you almost certainly don't want to do this, and I'm just showing that it is possible.
def make_house_class(brick_type):
class NewHouse(House):
def __init__(self, number_bricks):
self.bricks = [brick_type() for i in range(number_bricks)]
return NewHouse
Now, you could statically create all the house types:
StrongHouse = make_house_class(StrongBrick)
CheapHouse = make_house_class(CheapHouse)
# ...
… or maybe build them dynamically from a collection of all of your brick type:
brick_types = (StrongBrick, CheapBrick)
house_types = {brick_type: make_house_class(brick_type) for brick_type in brick_types}
… or even add some hacky introspection to just create a new FooHouse type for every FooBrick type in the current module:
for name, value in globals().items():
if name.endswith('Brick') and name != 'Brick' and isinstance(value, type):
globals()[name.replace('Brick', 'House')] = make_house_class(value)
… or even create them on the fly as needed in the factory-maker:
def make_house_factory(brick_type):
house_type = make_house_class(brick_type)
def factory(number_bricks):
return house_type(number_bricks, brick_type)
return factory
… or even the generated factory:
def make_house_factory(brick_type):
def factory(number_bricks):
return make_house_class(brick_type)(number_bricks, brick_type)
return factory
Add a parameter to the House.__init__ so that you can specify the Brick type:
import functools
class Brick():
def __init__(self):
self.weight = 1
class StrongBrick():
def __init__(self):
self.weight = 10
class House():
def __init__(self, number_bricks,brick_type=Brick):
self.bricks = [brick_type() for i in range(number_bricks)]
def get_weight(self):
return reduce(lambda x,y: x+y, [brick.weight for brick in self.bricks])
#not a new class, but an alias with a different default brick_type
StrongHouse = functools.partial(House,brick_type=StrongBrick)

Managing Instances in Python

I am new to Python and this is my first time asking a stackOverflow question, but a long time reader. I am working on a simple card based game but am having trouble managing instances of my Hand class. If you look below you can see that the hand class is a simple container for cards(which are just int values) and each Player class contains a hand class. However, whenever I create multiple instances of my Player class they all seem to manipulate a single instance of the Hand class. From my experience in C and Java it seems that I am somehow making my Hand class static. If anyone could help with this problem I would appreciate it greatly.
Thank you,
Thad
To clarify: An example of this situation would be
p = player.Player()
p1 = player.Player()
p.recieveCard(15)
p1.recieveCard(21)
p.viewHand()
which would result in:
[15,21]
even though only one card was added to p
Hand class:
class Hand:
index = 0
cards = [] #Collections of cards
#Constructor
def __init__(self):
self.index
self.cards
def addCard(self, card):
"""Adds a card to current hand"""
self.cards.append(card)
return card
def discardCard(self, card):
"""Discards a card from current hand"""
self.cards.remove(card)
return card
def viewCards(self):
"""Returns a collection of cards"""
return self.cards
def fold(self):
"""Folds the current hand"""
temp = self.cards
self.cards = []
return temp
Player Class
import hand
class Player:
name = ""
position = 0
chips = 0
dealer = 0
pHand = []
def __init__ (self, nm, pos, buyIn, deal):
self.name = nm
self.position = pos
self.chips = buyIn
self.dealer = deal
self.pHand = hand.Hand()
return
def recieveCard(self, card):
"""Recieve card from the dealer"""
self.pHand.addCard(card)
return card
def discardCard(self, card):
"""Throw away a card"""
self.pHand.discardCard(card)
return card
def viewHand(self):
"""View the players hand"""
return self.pHand.viewCards()
def getChips(self):
"""Get the number of chips the player currently holds"""
return self.chips
def setChips(self, chip):
"""Sets the number of chips the player holds"""
self.chips = chip
return
def makeDealer(self):
"""Makes this player the dealer"""
self.dealer = 1
return
def notDealer(self):
"""Makes this player not the dealer"""
self.dealer = 0
return
def isDealer(self):
"""Returns flag wether this player is the dealer"""
return self.dealer
def getPosition(self):
"""Returns position of the player"""
return self.position
def getName(self):
"""Returns name of the player"""
return self.name
From my experience in C and Java it seems that I am somehow making my Hand class static.
Actually, that is basically what you're doing. Well, not really making the class static, but making the variable static.
When you write declarations like this:
class Hand:
cards = []
that variable (cards) is associated with the class, not with the instance. To make an analogy to Java, every statement in a Python class that isn't part of a method of that class basically runs in a static initializer. You could almost think of it like this:
class Hand {
static {
cards = new object[];
}
}
(merely a rough analogy, of course)
To create an instance variable in Python, you have to set it as an attribute of the instance, which requires you to wait until you have a reference to the instance. In practice, this means you initialize it in the constructor, like so:
class Hand:
def __init__(self):
self.cards = []
Your problem is quite simple
if you assign lists to the body of python classes, when you append items to it, they will be store at Class level, not at instance level.
you can solve this problem by adding the line:
def __init__(self):
self.cards = []
this is a very known case of python pitfall, and I recommend you the reading:
http://zephyrfalcon.org/labs/python_pitfalls.html
As other answers noted, you were confused about class variables vs. instance variables. I suggest you review the basics of how Python classes work. Here is an answer I wrote for another question; reading this might help you.
How to define a class in Python

Categories

Resources