test equality for pygame.font.Font - python

I was trying to make a font cache like this:
from ._utils import SizedDict
def copysurf(x,mod):
if mod:
return cs(x)
return ImmutableSurface(x)
class FontCache:
def __init__(self):
self.cache = SizedDict(20)
self.fmisses = 0
self.fhits = 0
self.cmisses = {}
self.chits = {}
def getfor(self,font,char,aa,color):
if font not in self.cache:
self.cmisses[font] = 0
self.chits[font] = 0
self.cache[font] = SizedDict(300)
if char not in self.cache[font]:
self.cache[font][char] = font.render(char,aa,color)
return self.cache[font][char].copy()
fontCache = FontCache()
(Sized dicts are also made by me,they are dicts that deletes the oldest entry if it exeeds the capacity specified in the constructor.)
But the problem arises when i tried to cache the font.You see,
pygame.font.SysFont("Courier",22) == pygame.font.SysFont("Courier",22)
is false,and the hash() function returned different values.As i know,there aren't any methods that returns the original NAME of the font,and we can't test equality by merely knowing the size and the bold and italic flags.
Any thoughts?

You could subclass pygame.font.Font, store the metadata when the font is created. Something like this:
import pygame
pygame.init()
class Font(pygame.font.Font):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.properties = (args, tuple(kwargs.items()))
def __hash__(self):
return hash(self.properties)
def __eq__(self, other):
return self.properties == other.properties
#classmethod
def construct(cls, fontpath, size, bold, italic):
font = cls(fontpath, size)
font.strong = bold
font.oblique = italic
return font
# Prints True
print(
pygame.font.SysFont("Courier", 22, constructor=Font.construct)
== pygame.font.SysFont("Courier", 22, constructor=Font.construct)
)

Related

Can't loop through dict_keys in #property method in python 3?

So I have the following code:
#property
def mod_list(self) -> List[Modifier]:
mods = []
print(len(self.statuses)) #Prints 0??? Update method prints the actual number when called??? Also means it *is* getting called properly when it's getting accessed
for status in self.statuses: # I've tried calling the keys() method on the dict but that doesn't work either
print("hello") #Doesn't print, indicating that it isn't looping
mods.extend(status.mods) # Note: statuses dict uses StatusEffect objects as keys, with values being the number of turns left before that status is removed; StatusEffects all possess a 'mods' property that is initialized to '[]' and can only be made up of modifiers
return mods
I don't understand why it can't access the keys of the dict? Even if I remove the decorator and call it instead of accessing it?
Especially when this method works properly?
def update(self):
deletion = []
print(len(self.statuses)) #Prints actual number of keys????
for name in self.statuses.keys():
print(name.name, self.statuses[name]) #Prints normally whenever update is called???
if hasattr(name, "turn_effect"):
name.turn_effect(self.entity)
self.statuses[name] -= 1
if self.statuses[name] < 1:
deletion.append(name)
...
for status in deletion:
del self.statuses[status]
Why isn't it working properly? And how do I fix it?
Edit: I managed to recreate the issue below, I think it might have to do with 'deepcopy' in the spawn method since I couldn't recreate the issue from scratch until I implemented and used the spawn method.
from __future__ import annotations
from typing import Dict, List
from copy import copy, deepcopy
class Entity:
def __init__(self, name:str, **kwargs:Component):
self.name = name
self.components:Dict[str, Component] = {}
for name, component in kwargs.items():
self.add_component(name, component)
def add_component(self, name:str, component:Component):
self.components[name] = component
component.entity = self
def update(self):
for comp in self.components.values():
comp.update()
def spawn(self):
return deepcopy(self)
class Component:
__entity: Entity
#property
def entity(self) -> Entity:
return self.__entity
#entity.setter
def entity(self, entity:Entity):
if hasattr(self, "__entity") and self.__entity is not None:
self.entity.remove_component(self)
self.__entity = entity
def update(self):
"""Placeholder method for component update methods"""
class StatusList(Component):
entity: Entity
def __init__(self) -> None:
self.statuses:Dict[StatusEffect, int] = {}
def add_status(self, status:StatusEffect, turns:int=1):
self.statuses[status] = turns
def update(self):
deletion = []
print(len(self.statuses.keys()))
for name in self.statuses.keys():
print(name.name, self.statuses[name])
if hasattr(name, "turn_effect"):
name.turn_effect(self.entity)
self.statuses[name] -= 1
if self.statuses[name] < 1:
deletion.append(name)
for status in deletion:
del self.statuses[status]
#property
def mod_list(self) -> List[Modifier]:
mods = []
print(len(self.statuses))
for status in self.statuses:
print("hello")
mods.extend(status.mods)
return mods
class StatusEffect:
name:str
turn_effect: function
mods:List[Modifier] = []
def apply(self, entity:Entity, turns:int=1):
if "status_list" in entity.components.keys():
entity.components["status_list"].add_status(self.copy(), turns)
def copy(self): #I specifically defined this method in the original code in case I need to modify it in the future
return copy(self)
class StatList(Component):
entity: Entity
stat_record: List[Stat] = []
def __init__(self, **stats:Stat) -> None:
for name, stat in stats.items():
stat.stat_list = self
stat.name = name
self.stat_record.append(stat)
def get_stat(self, name:str) -> Optional[Stat]:
for stat in self.stat_record:
if name == stat.name:
return stat
def get_stat_name(self, stat:Stat) -> Optional[str]:
if stat in record:
return stat.name
class Stat:
name:str
base_value:int
def __init__(self, base:int=0):
self.base_value = base
#property
def entity(self) -> Entity:
return self.stat_list.entity
#property
def current_value(self) -> int:
value = self.base_value
for mod in self.get_modifiers():
value += mod.value
return int(value)
def get_modifiers(self):
for component in self.entity.components.values():
if hasattr(component, "mod_list"):
for mod in component.mod_list:
if mod.stat == self.name:
yield mod
class Modifier:
stat: str
value: Union[int, float]
def __init__(self, stat:str, value:Union[int, float]):
self.stat = stat
self.value = value
rage = StatusEffect()
rage.name = "Rage"
rage.turn_effect = lambda entity : print(f"{entity.name} is enraged")
rage.mods = [
Modifier("atk", 5)
]
player = Entity(
name="Player",
stat_list=StatList(atk=Stat(5)),
status_list=StatusList()
).spawn()
rage.apply(player, 10)
while True:
player.update()
player.components["stat_list"].get_stat("atk").current_value
input()
Unfortunately, using copy() in the spawn method would result in entities created that way sharing status effects, stats, etc., which really defeats the purpose of spawning new entities
Edit 2: Modified spawn method to use copy and to copy all components, have to add guard clauses now but it works.

A method inside a class that calls another method

I need help with the below code. I want to use the get_skies, get_high, and get_low method to call the set_skies, set_high, and set_low methods, respectively, and then return the value for init_skies, init_high, and init_low, respectively.
This is what I have got so far:
class WeatherForecast():
def set_skies(self, init_skies):
return init_skies
def set_high(self, init_high):
return init_high
def set_low(self, init_low):
return init_low
def get_skies(self):
self.set_skies()
def get_high(self):
self.set_high()
def get_low(self):
self.set_low()
In python attributes of class are publically accessible.
You don't need to use getter or setters for attributes unless you want to perform some kind of preprocessing or mutation of the attribute
In your case, you can try this,
class WeatherForecast():
def __init__(self, init_skies, init_low, init_high):
self._init_skies = init_skies
self._init_low = init_low
self._init_high = init_high
#property
def skies(self):
return self._init_skies
#property
def high(self):
return self._init_high
#property
def low(self):
return self._init_low
#skies.setter
def skies(self, value):
self._init_skies = value
#high.setter
def high(self, value):
self._init_high = value
#low.setter
def low(self, value):
self._init_low = value
w = WeatherForecast(1, 2, 3)
print(w.skies, w.low, w.high) # --> print the values
# Set the values
w.skies = 10
w.low = 20
w.high = 30
print(w.skies, w.low, w.high) # --> print the updated values

Checking if contained object has changed

I am working on creating a module with a class that acts as a container for a list of another created class. Is there a way for the container class to be able to tell if any of the objects it contains has changed?
Here is an example:
class Part:
def __init__(self, size):
self.part_size = size
class Assembly:
def __init__(self, *parts):
self.parts = list(parts) # `parts` are all Part() objects
self.update()
def update(self):
self.assy_size = 0
for each in self.parts:
self.assy_size += each.part_size
def __getitem__(self, key):
return self.parts[key]
This is what I get if I try to change any of the Part properties in the Assembly:
>>>x = Part(1)
>>>y = Part(1)
>>>z = Part(1)
>>>u = Assembly(x, y, z)
>>>u.assy_size
3
>>>u[0].part_size = 4
>>>u.assy_size
3
I know that I can create additional methods that will call the update method if I replace, delete, or add Part objects to the Assembly, but is there any way to have the Assembly notified if any of the contained Part properties have changed?
The answer is in your question. Use a property.
class Part:
_size = 0
assembly = None
#property
def part_size(self):
return self._size
#part_size.setter
def part_size(self, value):
self._size = value
if self.assembly: # only notify if an Assembly is set
self.assembly.update()
def set_assembly(self, assembly):
self.assembly = assembly
def __init__(self, size):
self.part_size = size
class Assembly:
def __init__(self, *parts):
self.parts = list(parts) # `parts` are all Part() objects
for part in self.parts:
part.set_assembly(self) # reference to self needed to notify changes
self.update()
def update(self):
self.assy_size = 0
for each in self.parts:
self.assy_size += each.part_size
In this version of Assembly the constructor sets a reference on the Part to itself. This way it can update the assembly when the part_size changes. Use it as the example in your question.
>>>x = Part(1)
>>>y = Part(1)
>>>z = Part(1)
>>>u = Assembly(x, y, z)
>>>u.assy_size
3
>>>u[0].part_size = 4
>>>u.assy_size
6
If update isn't an expensive operation (in your example it isn't, but maybe in reality you have thousands of parts), you could calculate the size ad-hoc using a property:
class Assembly:
def __init__(self, *parts):
self.parts = list(parts)
#property
def assy_size(self):
result = 0
for each in self.parts:
result += each.part_size
return result
which can be accessed the same way: assembly.assy_size.
The calculation can also be simplified:
#property
def assy_size(self):
return sum(part.part_size for part in self.parts)

Print dict with custom class as values wont call their string method?

I was messing around with classes in python and wrote 2 little ones:
class ClaElement:
start = None
end = None
basesLeft = None
orientation = None
contig = None
size = None
def __init__(self, contig, start, end, orientation, basesLeft=None):
self.contig = contig
self.start = start
self.end = end
self.orientation = orientation
self.basesLeft = basesLeft
self.size = self.end - self.start
def __str__(self):
return "{ClaElement: "+str(self.contig)+"_"+str(self.start)+"_"+str(self.end)+"_"+str(self.orientation)+"}"
def getSize(self):
return self.size
class ClaCluster:
contig = None
clusterElements = []
def __init__(self, contig, firstElement):
self.contig = contig
self.addElement(firstElement)
def addElement(self, claElement):
self.clusterElements.append(claElement)
def getFirst(self):
return self.clusterElements[0]
def getLast(self):
return self.clusterElements[-1]
def getElements(self):
return self.clusterElements
def getContig(self):
return self.contig
def __str__(self):
return "{ClaCluster: "+str(self.contig)+" "+str(len(self.clusterElements))+" elements}"
And my test-main:
from ClaElement import ClaElement
from ClaCluster import ClaCluster
if __name__ == '__main__':
ele = ClaElement("x",1,2,"left")
claDict = dict()
cluster = ClaCluster("x", ele)
claDict["hello"] = cluster
print(claDict)
print(claDict["hello"])
print(ele)
This leads to the following output:
{'hello': <ClaCluster.ClaCluster object at 0x7fe8ee04c5f8>}
{ClaCluster: x 1 elements}
{ClaElement: x_1_2_left}
Now my question is why is the output of my first print the memory address even though I provided a functioning string-method for my class ClaCluster? Is there a way to get the method invoked when I am printing the dictionary or do I have to iterate by hand?
The __str__() method of the built-in dict type uses the __repr__() method of your class, not __str__(). Simply rename your method, and all should work fine.

Python accessing instance variable dynamically in method

I am trying to add a value to a instance list in python but want to access it dynamically from within a method.
I cannot use dictionaries as I am trying to speed up the sorting of separate lists (boxes) rather than one large list.
Can anybody show me the correct way to do the following?
class Boxes:
def __init__(self):
self.box1 = []
self.box2 = []
#....
self.box10 = []
def addToBox(self, value):
box = self.determineBoxToUse(value)
## box = 2
varname = "box", box ## box2
self.varname.insert(0, value)
def determineBoxToUse(self, value):
## for this example returns 2
return 2
def dumpBox(self):
print self.box2
Boxes = Boxes();
Boxes.addToBox("123.001234")
Boxes.dumpBox()
Error: AttributeError: Boxes instance has no attribute 'varname'
Thanks
You can use hasattr and getattr, although many might suggest you should pursue a different solution.
def addToBox(self, value):
box = self.determineBoxToUse(value)
## box = 2
varname = "box{}".format(box) ## box2
if hasattr(self, varname):
getattr(self, varname).insert(0,value)
Demo:
>>> class Boxes:
def __init__(self):
self.box1 = []
self.box2 = []
#....
self.box10 = []
def addToBox(self, value):
box = self.determineBoxToUse(value)
## box = 2
varname = "box{}".format(box) ## box2
if hasattr(self, varname):
getattr(self, varname).insert(0,value)
def determineBoxToUse(self, value):
## for this example returns 2
return 2
def dumpBox(self):
print self.box2
>>> Boxes = Boxes()
>>> Boxes.addToBox("123.001234")
>>> Boxes.dumpBox()
['123.001234']
>>>

Categories

Resources