Why append() is called every loop? - python

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.

Related

Nested classes — can't understand their functionality

I'm learning about classes in Python, particularly about nested classes.
I'm trying to execute the below code and I get an error: int object is not callable, but
I don't understand why!
All I want is to create an object that identify Man, and he has hands, and the hands have their own size, length, etc...
I want to be able to set the hand size and get its value in the most elegant and easy way as possible and nothing work for me... I tried the below code and I really thought it would work but it didn't and now I know that "I Don't know" what to do for real.
class Man:
def __init__(self, name):
self.name = name
self.hand = self.Hand_Object() # Here we reference an Object called
# "hand" to the subvlass "Hand_Object".
def length(self , length):
self.length = length
def handsize(self, size=None): # This "handsize()" function will call the
# subclass function "length()" out from the
# Hand_Object vlass when it will be issued
# in the program.
if size==None:
return = self.hand.length()
else:
self.hand.length(size) # The "length()" function of the "Hand_Object"
# class requires a variable, so when we call
# that function we need to add a variable to it.
class Hand_Object:
def length(self, length=None):
if length == None:
return self.length
else:
self.length = length
def fingers(self, fingers):
self.fingers = fingers
myman = Man('shlomi')
myman.handsize(6)
print(myman.handsize()) # Here I get the error.
The issue is the line self.length = length in Hand_Object. You're overwriting the function length with an integer. You should call the function and the variable something different.

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

Python libtcod: Object description feature error

I'm working my way through the libtcod python tutorial, I've decided to mess around with some of the code to make it more unique today, and decided to start off with a feature to allow the player to hover the mouse over an object and press 'd' for a description of that object.
I'm currently running into an attribute error: 'str' object has no attribute 'describe' line 657. I've tried many different things but notihng seems to work, unfortunately my level of understanding is pretty limited right now so I can't figure out what's going wrong.
Here are the relevant classes and functions:
class Object:
#this is a generic object: the player, a monster, an item, the stairs...
#it's always represented by a character on screen.
def __init__(self, x, y, char, name, color, blocks=False, fighter=None, ai=None, item=None, description=None):
self.x = x
self.y = y
self.char = char
self.name = name
self.color = color
self.blocks = blocks
self.fighter = fighter
if self.fighter: #let the fighter component know who owns it
self.fighter.owner = self
self.ai = ai
if self.ai: #let the ai component know who owns it
self.ai.owner = self
self.item = item
if self.item: #let the item component know who owns it, like a bitch
self.item.owner = self
self.description = self
if self.description: #let the description component know who owns it
self.description.owner = self
def describe(self):
#describe this object
message(self.description, libtcod.white)
def handle_keys():
global keys;
if key_char == 'd':
#show the description menu, if an item is selected, describe it.
chosen_object = description_menu('Press the key next to an object to see its description.\n')
if chosen_object is not None:
chosen_object.describe()
return 'didnt-take-turn'
def description_menu(header):
global mouse
#return a string with the names of all objects under the mouse
(x, y) = (mouse.cx, mouse.cy)
#create a list with the names of all objects at the mouse's coordinates and in FOV
names = [obj.name for obj in objects if obj.x == x and obj.y == y and libtcod.map_is_in_fov(fov_map, obj.x, obj.y)]
names = ', '.join(names) #join the names, seperated by commas
return names.capitalize()
#show a menu with each object under the mouse as an option
if len(names) == 0:
options = ['There is nothing here.']
else:
options = [item.name for item in names]
index = menu(header, options, INVENTORY_WIDTH)
#if an item was chosen, return it
if index is None or len(names) == 0: return None
return names[index].description
Any help would be much appreciated!
The function description_menu() has the following return
names[index].description
This is a string member that belongs to Object.
When you say
chosen_object.describe()
You are calling the describe() method, but that belongs to the Object class, not a string (hence the attribute error: 'str' object has no attribute 'describe'). You would have to have description_menu() return the Object instead of just the name of it.

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.

Global variable defining fails

I have created a simple renaming script but I would like to ask for some advice so that I can refine the coding as well as honing my python scripting. Below is a small portion of code for now...
Though this may not be an issue in my point of view, but other than the two functions I have stated below, I have came to realize that almost all my functions, they contains objects = cmds.ls(selection=True) Though I do not mind retyping over and over again but I do believe there is a better way to rectify this problem.
However, when I tried to make them global before the class function, it is able to run until when I tired to execute one of the functions, it prompts an error saying that global name 'objects' is not defined or 'objects are not defined' etc.
Pertaining to that, any suggestions?
class mainWindow(QDialog):
def __init__(self, parent=None):
super(mainWindow, self).__init__(parent)
self.resize(300,225)
self.initUI()
self.createConnections()
def searchReplace(self):
wordSearch = str(self.searchTxt.text())
wordReplace = str(self.replaceTxt.text())
objCnt = cmds.ls(sl=True, sn=True)
if len(objCnt) == 0:
self.searchTxt.clear()
self.replaceTxt.clear()
cmds.warning('Nothing is selected')
else:
for wordString in sorted(objCnt):
if wordSearch in wordString:
newWordString = wordString.replace(wordSearch, wordReplace)
cmds.rename(wordString, newWordString)
self.searchTxt.clear()
self.replaceTxt.clear()
print '%s' %wordString + " has changed to : " + "%s" %newWordString
def addPrefix(self):
objects = cmds.ls(selection=True)
pfx = str(self.prefixTxt.text())
for item in objects:
if pfx == "":
cmds.warning('No prefix values in the field')
else:
cmds.rename(item, pfx + "_" + item)
self.prefixTxt.clear()
print 'Prefix added: %s_' %pfx
def addSuffix(self):
objects = cmds.ls(selection=True)
sfx = str(self.suffixTxt.text())
for item in objects:
cmds.rename(item, item + "_" + sfx)
self.suffixTxt.clear()
print 'Suffix added: _%s' %sfx
def numPadding(self):
objects = pm.ls(selection=True)
num = self.numTxt.text()
padding = self.paddingTxt.text()
if num != "" and padding !="":
try:
for currentWordStr in objects:
pad = ("%%0%ii" % int(padding)) % int(num)
newWordStr = currentWordStr.rename(currentWordStr.name() + "_" + pad)
except Exception:
self.numTxt.clear()
self.paddingTxt.clear()
cmds.warning('Input numerical values only')
else:
cmds.warning('Entries of Num or Padding are empty')
def selectHierarchy(self):
sel = cmds.ls(selection = True)
selCnt = len(sel)
if int(selCnt) == 0:
cmds.warning('Nothing is selected')
else:
objHierarchy = cmds.listRelatives(ad=True, type='transform', fullPath=True)
cmds.select(sel, objHierarchy)
def clearHierarchy(self):
sel = cmds.ls(selection = True)
selCnt = len(sel)
if int(selCnt) != 0 :
objHierarchy = cmds.select(clear=True)
else:
cmds.warning('Selection is empty. Nothing to be cleared')
All right, I think I understand what you tried, going to take a shot at an answer.
First, take a look at the following posts, should get you up to speed on globals:
Using global variables in a function other than the one that created them (great, succinct summary)
Variable scope outside of classes (example with classes)
So, first off, you don't need to use the global keyword when first declaring objects outside of the class definition. So, instead of:
global objects
objects = cmds.ls(selection=True)
class mainWindow(QDialog):
...
You would do:
objects = cmds.ls(selection=True)
class mainWindow(QDialog):
...
Then, your functions can just refer to "objects". If you need to WRITE to objects from within your functions in the class, then you need to first use the global keyword (this code assumes objects was defined before the class):
def my_method(self):
global objects
objects = some_function()
That said, I'm not 100% sure how the above code is being invoked, so it's possible that something else is causing "objects" to be undefined.
You might be better served with a class attribute here. You could do this:
class mainWindow(QDialog):
objects = cmds.ls(selection=True)
def my_func(self):
for item in self.objects:
do_stuff()
Keep in mind that objects would be the same for all instances of mainWindow, and any updates to objects in one instance will affect all other instances. That should be fine from what I can tell, but you should definitely become familiar with instance vs. class vs. module.
Hope that helps!
UPDATE: Whoops, changed the class attribute in one place, but not the other in the last example. Updated the example, it should make way more sense now.

Categories