Python - AttributeError - python

So for a line class I'm doing, I keep getting an error that says
AttributeError: Line instance has no attribute 'point0'
I'm declaring the line like this:
def __init__(self, point0, point1):
self.x = point0
self.y = point1
def __str__(self):
return '%d %d' % (int(round(self.point0)), int(round(self.point1)))
And I get the x and y from my point class which should already be float values so I don't need to check for an error in my init method however I do check to see if point0 and point1 are floats in my rotate method:
def rotate(self, a):
if not isinstance(a, float) or not isinstance(self.point0, float) or not isinstance(self.point1, float):
raise Error("Parameter \"a\" illegal.")
self.point0 = math.cos(a) * self.point0 - math.sin(a) * self.point1
self.point1 = math.sin(a) * self.point0 + math.cos(a) * self.point1
So why does python keep saying that it has no attribute point0? I also tried changing my init method to look like this:
def __init__(self, point0, point1):
self.point0 = point0
self.point1 = point1
But when I do that the error says point0 has no attribute float. So why do I keep getting this error? Here's the code I'm using to test:
p0 = Point(0.0, 1.0)
p1 = Point(2.0, 3.0)
line = Line(p0,p1)
print line

I'm curious... how much do you know about scope in Python?
In your class, you have a member variable named x and another named y. Your init function accepts an argument called point0 and another called point1. It saves point0 in the x member variable, and point1 in y. Then, in your rotate function, you attempt to access a variable called point0. Do you see the problem?
An important thing to understand when programming (and this is true in most programming languages, if not all of them) is that the name of an argument doesn't affect the name of that data elsewhere. I can pass a variable called foo into a function that takes an argument called bar. In that function, I have to refer to the data as bar because that's the name of the variable. Later, after I've called that function, the name of the variable is still foo, because only the variable inside the function is called bar. Does that make sense?

your class accept point0 and point1 parameters when you call it. If you want to get values of these parameters you should use self.x(for point0) and self.y(for point1)
or another way;
class Line:
def __init__(self, point0, point1):
self.point0 = point0
self.point1 = point1
I suggest you to read;
Python __init__ and self what do they do?
https://www.ibiblio.org/swaroopch/byteofpython/read/class-init.html
https://docs.python.org/2/tutorial/classes.html

A few overall points before displaying your corrected code. (Note that not much actually changed):
Don't bother checking argument types. Python programmers are assumed to be responsible enough to read the documentation and pass values of the correct value.
Your Line class was duplicating code that you had already defined in the Point class. The attributes of a line are Point instances, so you can use the methods you defined to implement the Line methods.
There's no reason to round the point coordinates to integers when displaying them; show the actual floating-point values that define the point. Your Line.__str__ method can take advantage of the fact that you've defined Point.__str__.
And now, your much shorter and corrected code, with some interspersed comments.
import math
class Point:
def __init__(self, x, y):
'''x and y should be floats'''
self.x = x
self.y = y
def rotate(self, a):
'''Rotate the point around the origin by a radians'''
self.x = math.cos(a) * self.x - math.sin(a) * self.y
self.y = math.sin(a) * self.x + math.cos(a) * self.y
# If you *were* going to check if a is a float, you
# need to do it *before* you use it.
def scale(self, f):
'''Scale the point by f units''' # you get the idea
self.x = f * self.x
self.y = f * self.y
def translate(self, delta_x, delta_y):
self.x = self.x + delta_x
self.y = self.y + delta_y
def __str__(self):
# If you're storing floats, it's probably useful
# to output them.
return '(%f, %f)' % (self.x, self.y)
# Operations on a line all involve applying the same operations
# to each of its end points.
class Line:
def __init__(self, point0, point1):
self.point0 = point0
self.point1 = point1
def rotate(self, a):
self.point0.rotate(a)
self.point1.rotate(a)
def scale(self, factor):
self.point0.scale(factor)
self.point1.scale(factor)
# If for whatever reason you didn't want to use Point.scale
# here, the code would be...
# self.point0.x = f * self.point0.x
# self.point0.y = f * self.point0.y
# self.point1.x = f * self.point0.x
# self.point1.y = f * self.point0.y
def translate(self, delta_x, delta_y):
self.point0.translate(delta_x, delta_y)
self.point1.translate(delta_x, delta_y)
def __str__(self):
# You've already defined out to turn a Point into
# a string, so you can take advantage of that here.
return "%s -- %s" % (self.point0, self.point1)

I'm going to add another answer here, both because I lack the reputation to comment on the other answer and because I feel this answer is unrelated to my previous answer (which addressed a different problem than what you're seeing now).
So. That said, look at this line of code:
return '%d %d' % (int(round(self.point0)), int(round(self.point1)))
round is a function that takes a numeric argument. However, self.point0 and self.point1 are not numbers. They are points. If you want the numbers from them, you'll have to refer to those explicitly (i.e. self.point0.x).

Related

Why append() is called every loop?

I have got the tile class:
class Tile:
tile_pref = {
"sprites": []
}
pos = Vector2(vector_zero)
def img(self, x, y, tilemap, t):
return self.tile_pref["sprites"][0]
def __init__(self, img, pos):
self.tile_pref["sprites"].append(img)
self.pos = pos
And another part, cropped:
src is valid string path, spacing, tile_size, offset are vectors with .x and .y.
self.__tiles__ is array.
img = pyglet.image.load(src)
for i in range((img.height - offset.x) // (spacing.x + tile_size.x) + 1):
for j in range((img.width - offset.y) // (spacing.y + tile_size.y) + 1):
a = offset.x + (tile_size.x + spacing.x) * j
b = offset.y + (tile_size.y + spacing.y) * i
self.__tiles__.append(Tile(img.get_region(a, b, tile_size.x, tile_size.y), Vector2(i, j)))
Main problem is in what tile_pref["sprites"] is not 1 in length, bu equals to all amount of tiles. How can I fix it?
I think, when I call img.get_region, it returns me a link to it. So, it calls append any time link is changing.
Here:
class Tile:
tile_pref = {
"sprites": []
}
pos = Vector2(vector_zero)
You are defining tile_pref and pos as class attributes - attributes that belong to the class (not to the instances of...), and are shared amongst all instances. Unless shadowed by an instance attribute, class attributes are also available thru the instance (which is how you can access methods, which ARE class attributes), so here:
def __init__(self, img, pos):
self.tile_pref["sprites"].append(img)
you are actually mutating the class's own tile_pref attribute - so whatever you add here will be visible to all instances.
Note that the following line:
self.pos = pos
creates a 'pos' instance attribute (you are binding the name, not mutating), which will shadow the class attribute.
For what it's worth, all this is clearly and explicitely documented in the official tutorial - so I kindly suggest you stop what you're doing, take a day (or two if needed) to do the whole tutorial, then come back to your code. This will save you a lot of time and pain and frustration.

Printing data from python program to terminal on mac

I'm trying to get my code to output the results of really any code to my terminal, but I can't seem to find out why it won't print. I'm just learning to code so I've been finding a lot of explanation on this site kind of confusing, so apologies if this has been asked before.
This is my python file python.py:
class point(object):
def __init__(self, x, y):
self.x = float(x);
self.y = float(y);
def __str__(self):
return("(" + self.x + "," + self.y + ")")
def main():
first = point(2,3)
if __name__ == '__main__':
main()
and in the terminal I'm just typing "python python.py"
Add a print statement in the main() function to print to terminal:
class point(object):
def __init__(self, x, y):
self.x = float(x);
self.y = float(y);
def __str__(self):
return("(" + self.x + "," + self.y + ")")
def main():
first = point(2,3)
print(first)
if __name__ == '__main__':
main()
As others have mentioned, you need to add a print() call in your main function in order to get output to the terminal. Additionally, a few other things to note: Python doesn't require semi-colons, so there's no need for them in your __init__() method and you can't implicitly convert floats to strings, so you have to be explicit in your __str__() method.
class point(object):
def __init__(self, x, y):
self.x = float(x)
self.y = float(y)
def __str__(self):
# explicitly cast floats to strings
return "(" + str(self.x) + "," + str(self.y) + ")"
def main():
first = point(2,3)
print(first)
if __name__ == '__main__':
main()
Also, as MrTrustworthy pointed out, you don't need to call Class.__str__() directly as print first calls the __str__() method associated with the class. If there's no such method and implicit conversion fails, then you will get the representation of the calling function and the address in memory.
If you remove __str__() from your example, you should get output like this: <__main__.point object at 0x7f184cec4b70> if you try to print the object.

Pygame, inheritance issue

I am either new nor experience in python and i find myself in a fat problem that even after many hours in Google and Bing i can find the answer for. My problem starts with this code:
class Rectangulo(object):
def __init__ (self, x, y, color, largo, alto, cambio_x, cambio_y):
self.alto = alto
self.largo = largo
self.color = color
self.cambio_x = cambio_x
self.cambio_y = cambio_y
self.x = x
self.y = y
def dibujar(self):
pygame.draw.rect(pantalla, self.color, (self.x, self.y, self.alto, self.largo))
def mover(self):
self.x += self.cambio_x
self.y += self.cambio_y
class Elipse(Rectangulo):
def __init__(self):
Rectangulo.__init__(self)
def dibujar (self):
pygame.draw.ellipse(pantalla, Rectangulo.color,(Rectangulo.x, Rectangulo.y, Rectangulo.alto, Rectangulo.largo))
THis is the most important piece of code right now. I have find the problem laid in Elipse and have tried many ways to make the inheritance to work, but the console continue showing this message.
Traceback (most recent call last):
File "/home/josh/Escritorio/Codigo python/Jueguito.py", line 63, in <module>
miEl = Elipse(x,y,VERDE,alto,largo,cam_x,cam_y)
TypeError: __init__() takes 1 positional argument but 8 were given
Everytime i tried to call the class function dibujar() with this code:
for item in range(10):
x = random.randrange(685)
y = random.randrange(485)
alto = random.randrange(20, 71)
largo = random.randrange(20, 71)
cam_x = random.randrange(1, 2)
cam_y = random.randrange(-3, 3)
miObjeto = Rectangulo(x,y,VERDE,alto,largo,cam_x,cam_y)
miLista.append(miObjeto)
miEl = Elipse(x,y,VERDE,alto,largo,cam_x,cam_y)
miEl variable used to have their own for loop but i thought in this way would be less confusing for me. I still can't figure out what is happening. I fear i need some help.
Your Eclipse instance is failing to initialise, because when it calls the initialise of its base class Rectangulo, that class takes 7 parameters (x, y color, ... etc) and you aren't providing any.
So you have a number of options, but the two most common approaches would be:
Pass the same 7 params into Eclipse's __init__ method, and then pass those same params into the call to Rectangulo.__init__(self, ...)
Decide on the params to be used for the Rectangulo within the Elipse e.g. Rectangulo.__init__(self, 1, 2, "red", ...).
Generally speaking you will probably want the first option. e.g.
class Elipse(Rectangulo):
def __init__(self, x, y, color, largo, alto, cambio_x, cambio_y):
Rectangulo.__init__(self, x, y, color, largo, alto, cambio_x, cambio_y)
If your Elipse class needs no additional parameters of its own, then you can simplify the above a little by doing this:
class Elipse(Rectangulo):
def __init__(self, *args, **kwargs):
Rectangulo.__init__(self, *args, **kwargs)
This will basically pass any and all arguments given to the instantiation of Elipse through to the Elipse.__init__ call.

Property set correctly inside an object but not accessible outside

I'm working under python pyramid, with Python3.
I have a model that looks like this:
class OneTimeCode(Base):
__tablename__ = 'otc_one_time_codes'
otc_one_time_code_id = Column(Integer, primary_key=True)
otc_one_time_code = Column(String(32))
otc_usr_user_id = Column(Integer, ForeignKey('usr_users.usr_user_id'), nullable=True)
otc_expire_time = Column(DateTime)
def __init__(self, otc_usr_user_id, otc_expire_time=None):
self.otc_usr_user_id = otc_usr_user_id
if otc_expire_time is None:
self.otc_expire_time = (datetime.now() + timedelta(6*365/12)).isoformat()
else:
self.otc_expire_time = otc_expire_time
#classmethod
def get_code(self, hlength=6):
seed = datetime.now() + timedelta(random.randrange(1,10000))
tmp_hash = hashlib.md5(seed.strftime("%Y-%m-%d %H:%M:%S.%F").encode('utf-8')).hexdigest()
if hlength == 32:
self.otc_one_time_code = tmp_hash
else:
self.otc_one_time_code = tmp_hash[0 : hlength]
print(self.otc_one_time_code)
The problem is, when I instantiate one of these objects and then explicitly call get_code, the print line at the end prints to the screen the code successfully.
However, in my view, if I explicitly try to print that property, it's 'None'
Here's what my view code looks like:
otc = OneTimeCode(
otc_usr_user_id = user.usr_user_id
)
otc.get_code()
pprint.pprint(vars(otc))
session.add(otc)
And the console output looks like this:
0d097c
{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x50877d0>, 'otc_expire_time': '2015-02-13T10:56:14.244447', 'otc_usr_user_id': 1} 2014-08-14 22:56:14,245
INFO [sqlalchemy.engine.base.Engine][Dummy-2] INSERT INTO otc_one_time_codes (otc_one_time_code, otc_usr_user_id, otc_expire_time) VALUES (%(otc_one_time_code)s, %(otc_usr_user_id)s, %(otc_expire_time)s) RETURNING otc_one_time_codes.otc_one_time_code_id 2014-08-14 22:56:14,245
INFO [sqlalchemy.engine.base.Engine][Dummy-2] {'otc_one_time_code': None, 'otc_expire_time': '2015-02-13T10:56:14.244447', 'otc_usr_user_id': 1} 2014-08-14 22:56:14,247
INFO [sqlalchemy.engine.base.Engine][Dummy-2] COMMIT
You can see the value inside the model: 0d097c, and also the pprint object, where it doesn't look like the property exists.
Why can't I get access to this property?
Looks like you should be using a #property instead of a OTC, however it also seems like this may be something you DON'T want to calculate each time!
# for all the docstrings, let multi = Multi(2)
class Multi(object):
def __init__(self, attribute):
"""When instantiated, set self.attribute to attribute"""
self.attribute = attribute
#property
def attribute_times_ten(self):
"""accessed via multi.attribute_times_ten
and will return 20. Use properties to signify
a variable that requires some work done to it
that needs to calculated each time it's called."""
return attribute_times_ten
#classmethod
def times_ten(cls, num):
"""Not the best example, but a #classmethod will
give the class as its first argument, NOT the
instance. This is useful in lots of constructor
settings, e.g. CreateClass.fromstring("attributes")"""
return num * 5
def generate_number(self, multiplier):
"""This is just a normal method. This is what I think
you want, tbh, and you should probably call it in your
__init__ method since you NEED this to run in your OTC
for it to work as intended. Methods (like properties)
are automagically passed the instance as the first
argument, so we can CHANGE self.attribute with that."""
self.attribute = self.attribute * multiplier
Docstrings should be self descriptive, but:
multi = Multi(2)
multi.attribute_times_ten # returns 20
Multi.times_ten(8) # returns 80, note the capital M!
multi.generate_number(3) # self.attribute is now 6
multi.attribute_times_ten # returns 60
A real-world case where you might need all of the above:
class _Tile(object):
def __init__(self, x, y):
"""A naive implementation of Tile that doesn't care
what its side length is and doesn't have any properties
to hide its attributes"""
self.x = x
self.y = y
#classmethod
def tiles_to_pixels(cls, tile):
return cls(tile._x * tile.side_length, tile._y * tile.side_length)
#classmethod
def tiles_to_tiles(cls, tile):
return cls(tile._x, tile._y)
class Tile(object):
def __init__(self, x, y, side_length):
"""A tile object in a map"""
self._x = x # x-coord in tiles
self._y = y # y-coord in tiles
self.side_length = side_length # pixels per tile
#property
def in_pixels(self):
"""self.in_pixels returns an object whose .x and .y
correspond to the x and y position IN PIXELS of the
top-left corner of the tile."""
_tile = _Tile.tiles_to_pixels(self)
return _tile
#property
def in_tiles(self):
"""self.in_tiles returns an object whose .x and .y
correspond to the x and y position IN TILES of the
top-left corner of the tile."""
_tile = _Tile.tiles_to_tiles(self)
return _tile
def change_side_length(self, new_length):
"""Use to change the side length. This can break
your whole map since it's naive, so be careful."""
self.side_length = new_length
my_tile = Tile(0,0,32) # 32 pixel tile starting at (0,0)
my_tile.x # NameError, since it's called my_tile._x
my_tile.in_tiles.x # 0
my_tile.in_pixels.y # 0
other_tile = Tile(4,7,32) # 32 pixel tile starting at (4,7)
other_tile.y # NameError, see above
other_tile.in_tiles.y # 7
other_tile.in_pixels.x # 128

How do I rewrite this function now that it needs to call upon a method in a class from a different file?

Here is the function (it's in a file, "worldmodel.py"):
def add_entity(world, entity):
pt = entities.get_position(entity)
if within_bounds(world, pt):
old_entity = occ_grid.get_cell(world.occupancy, pt)
if old_entity != None:
entities.clear_pending_actions(old_entity)
occ_grid.set_cell(world.occupancy, pt, entity)
world.entities.append(entity)
And here is the class in a file named, "occ_grid.py":
class Grid:
def __init__(self, width, height, occupancy_value):
self.width = width
self.height = height
self.cells = []
# initialize grid to all specified occupancy value
for row in range(0, self.height):
self.cells.append([])
for col in range(0, self.width):
self.cells[row].append(occupancy_value)
def set_cell(self, point, value):
self.cells[point.y][point.x] = value
My question is, how would I rewrite the line of code in "def add_entity" that refers to "set_cell"? (Now that I've made set_cell a method of the class Grid) NOTE: Before I made set_cell part of the grid class, it was a function outside of the class (but still in the same file as the class) Thanks!
You'll need to import occ_grid in your worldmodel.py, then instantiate a Grid object and call that objects set_cell()-method. The add_entity needs to get the Grid-object as its parameter unless it can safely instantiate new ones at will.
Here's a naive example which does not work but demonstrates what I mean:
import occ_grid
g = occ_grid.Grid(your_width, your_height, occupancy)
def add_entity(world, entity, grid):
pt = entities.get_position(entity)
if within_bounds(world, pt):
old_entity = grid.get_cell(world.occupancy, pt)
if old_entity != None:
entities.clear_pending_actions(old_entity)
grid.set_cell(world.occupancy, pt, entity)
world.entities.append(entity)
add_entity(world, entity, g)
Unless you make the set_cell function a static method of the Grid class, you're going to need and instance of Grid.
from occ_grid import Grid
I am going to make an assumption here, and say that your want your grid to be part of the world? Either way, this is an example of instantiating that class.
class World:
grid = Grid()
def add_entity(world, entity):
# All that other stuff.
world.grid.set_cell(pt, entity)
There are two issues here, (1) calling functions across modules and (2) calling methods of classes.
It seems you can already do (1).
The trick is that although methods are defined as
def methodName(self, ...)
They are called as
object.methodName(...)
And object implicitly becomes the "self" Parameter. Here is an example:
import occ_grid # Import the module (file) that contains Grid.
.
.
world.occupancy = occ_grid.Grid() # Create an instance of Grid.
.
.
def add_entity(world, entity):
pt = entities.get_position(entity)
.
.
world.occupancy.set_cell(pt, entity)
In this example, grid is a global variable, which is probably not a good design. I guess it should be a property of world, but that's only a guess.

Categories