I am building a battle simulation program. Running into TypeError. Unable to resolve. Any help would be appreciated. I can't seem to get the iadd function working. I'm trying to add new Pokemon objects to the existing PokemonTrainer object by using the iadd function in python. Anyone has any ideas on how to execute this?
main.py
name = input("State your name: ")
player = PokemonTrainer(name)
player += Pokemon("Staryu", ElementType.WATER, [
Move("water blast", ElementType.WATER, 5),
Move("water cyclone", ElementType.WATER, 6)
])
pokemon_trainer.py
For the iadd, I'm using type-based dispatch to deal with the parameter. If it is of type Pokemon, call the add_pokemon method on self and then return the self object. If it is of type Item, call the add_item method on self and then return the self object. Otherwise, raise a TypeError.
from pokemon import Pokemon
class PokemonTrainer:
def __init__(self, name, pokemon = [], items = [], current_active_pokemon = None):
self.name = name
self.pokemon = pokemon
self.items = items
self.current_active_pokemon = current_active_pokemon
def __iadd__(self, other):
self.pokemon.append(other)
if (type(other) == type(Pokemon)):
self.add_pokemon(other)
elif (type(other) == type(Item)):
self.add_item(other)
else:
raise TypeError("Only Pokemon or Item type are allowed")
return self
def add_pokemon(self, pkmn):
self.pokemon.append(pkmn)
if (self.current_active_pokemon == None):
self.current_active_pokemon = pkmn
def add_item(self, item):
self.items.append(item)
pokemon.py
class Pokemon:
def __init__(self, name, pokemon_type, moves):
self.name = name
self.pokemon_type = pokemon_type
self.moves = moves
self.level = 1
self.exp = 0
self.max_hp = 100
self.current_hp = self.max_hp
self.attack_power = 1
self.defense_power = 1
self.fainted = False
I guess you want isinstance(other, Pokemon) instead of type(other) == type(Pokemon).
https://docs.python.org/3/library/functions.html#isinstance
Also relevant here: What are the differences between type() and isinstance()?
I couldn't figure out why I'm getting a NameError when trying to access a function inside the class.
This is the code I am having a problem with. Am I missing something?
class ArmstrongNumber:
def cubesum(num):
return sum([int(i)**3 for i in list(str(num))])
def PrintArmstrong(num):
if cubesum(num) == num:
return "Armstrong Number"
return "Not an Armstrong Number"
def Armstrong(num):
if cubesum(num) == num:
return True
return False
[i for i in range(1000) if ArmstrongNumber.Armstrong(i)] # this return NameError
Error-message:
NameError Traceback (most recent call last)
<ipython-input-32-f3d39f24a48c> in <module>
----> 1 ArmstrongNumber.Armstrong(153)
<ipython-input-31-fd21586166ed> in Armstrong(num)
10
11 def Armstrong(num):
---> 12 if cubesum(num) == num:
13 return True
14 return False
NameError: name 'cubesum' is not defined
Use classname before method:
class ArmstrongNumber:
def cubesum(num):
return sum([int(i)**3 for i in list(str(num))])
def PrintArmstrong(num):
if ArmstrongNumber.cubesum(num) == num:
return "Armstrong Number"
return "Not an Armstrong Number"
def Armstrong(num):
if ArmstrongNumber.cubesum(num) == num:
return True
return False
print([i for i in range(1000) if ArmstrongNumber.Armstrong(i)])
Unlsess you pass self to the functions those functions are not instance methods. Even if you define that within class you still need to access them using classname.
this should be your actual solution if you really want to use class
class ArmstrongNumber(object):
def cubesum(self, num):
return sum([int(i)**3 for i in list(str(num))])
def PrintArmstrong(self, num):
if self.cubesum(num) == num:
return "Armstrong Number"
return "Not an Armstrong Number"
def Armstrong(self, num):
if self.cubesum(num) == num:
return True
return False
a = ArmstrongNumber()
print([i for i in range(1000) if a.Armstrong(i)])
output
[0, 1, 153, 370, 371, 407]
2nd method:
if you dont want to use class then use static methods like this
def cubesum(num):
return sum([int(i)**3 for i in list(str(num))])
def PrintArmstrong(num):
if cubesum(num) == num:
return "Armstrong Number"
return "Not an Armstrong Number"
def Armstrong(num):
if cubesum(num) == num:
return True
return False
# a = ArmstrongNumber()
print([i for i in range(1000) if Armstrong(i)])
I'm writing a test script and met this problem:
ERROR - FAILED - ERROR Message:'NoneType' object is not iterable
Code is here:
class tc_ID2****(****):
def test_run(self):
case_name = self.__class__.__name__
logger.info("========== Begin of Running Test Case %s ==========" % self.__class__.__name__)
try:
the_list = self.list_objects("main-window")
off_list = self.coveroff(the_list)
if 'lblname' in off_list:
self.assert_(True, case_name)
else:
raise FailException("Can't find the lable")
except Exception, e:
logger.error("FAILED - ERROR Message:" + str(e))
self.assert_(False, case_name)
finally:
self.capture_image(case_name)
self.restore_gui_environment()
logger.info("========== End of Running Test Case: %s ==========" % case_name)
def coveroff(self,the_list):
off_list=[]
for list_unit in the_list:
if isinstance(list_unit,list):
off_list.extend(list_unit)
else:
off_list.append(list_unit)
return off_list
if __name__ == "__main__":
unittest.main()
The list_object method will return a two-layer nested list.
def list_objects(self, window):
logger.info("get objects list in window: %s" % window)
all_objects_list = self.__parse_objects(ldtp.getobjectlist(self.get_locator(window)))
logger.info("sorted all_objects_list: %s" % all_objects_list)
def __parse_objects(self, objects_list):
logger.info("parse objects list")
window_list = []
tab_list = []
button_list = []
table_list = []
text_list = []
menu_list = []
checkbox_list = []
label_list = []
others_list = []
parsed_objects_list = [window_list, tab_list, button_list, table_list, text_list, menu_list, checkbox_list, label_list, others_list]
for item in objects_list:
if item.startswith("frm") or item.startswith("dlg"):
window_list.append(item)
elif item.startswith("ptab"):
tab_list.append(item)
elif item.startswith("btn"):
button_list.append(item)
elif item.startswith("ttbl") or item.startswith("tbl"):
table_list.append(item)
elif item.startswith("txt"):
text_list.append(item)
elif item.startswith("mnu"):
menu_list.append(item)
elif item.startswith("chk"):
checkbox_list.append(item)
elif item.startswith("lbl"):
label_list.append(item)
else:
others_list.append(item)
return parsed_objects_list
But I can't found things from a nested list.
So I made a method named coveroff to make the two-layer list become a simple list.
However this error just broke me down.
How can I debug this error? Any idea is welcome!
In Python, not having a return statement in your method is the same as returning None. So, your method is returning None when you expected it to return a list, thus the error.
I have an object (Person) that has multiple subobjects (Pet, Residence) as properties. I want to be able to dynamically set the properties of these subobjects like so:
class Person(object):
def __init__(self):
self.pet = Pet()
self.residence = Residence()
class Pet(object):
def __init__(self,name='Fido',species='Dog'):
self.name = name
self.species = species
class Residence(object):
def __init__(self,type='House',sqft=None):
self.type = type
self.sqft=sqft
if __name__=='__main__':
p=Person()
setattr(p,'pet.name','Sparky')
setattr(p,'residence.type','Apartment')
print p.__dict__
Currently I get the wrong output: {'pet': <__main__.Pet object at 0x10c5ec050>, 'residence': <__main__.Residence object at 0x10c5ec0d0>, 'pet.name': 'Sparky', 'residence.type': 'Apartment'}
As you can see, instead of setting the name attribute on the Pet subobject of the Person, a new attribute pet.name is created on the Person.
I cannot specify person.pet to setattr() because different sub-objects will be set by the same method, which parses some text and fills in the object attributes if/when a relevant key is found.
Is there a easy/builtin way to accomplish this?
Or perhaps I need to write a recursive function to parse the string and call getattr() multiple times until the necessary subobject is found and then call setattr() on that found subobject?
You could use functools.reduce:
import functools
def rsetattr(obj, attr, val):
pre, _, post = attr.rpartition('.')
return setattr(rgetattr(obj, pre) if pre else obj, post, val)
# using wonder's beautiful simplification: https://stackoverflow.com/questions/31174295/getattr-and-setattr-on-nested-objects/31174427?noredirect=1#comment86638618_31174427
def rgetattr(obj, attr, *args):
def _getattr(obj, attr):
return getattr(obj, attr, *args)
return functools.reduce(_getattr, [obj] + attr.split('.'))
rgetattr and rsetattr are drop-in replacements for getattr and setattr,
which can also handle dotted attr strings.
import functools
class Person(object):
def __init__(self):
self.pet = Pet()
self.residence = Residence()
class Pet(object):
def __init__(self,name='Fido',species='Dog'):
self.name = name
self.species = species
class Residence(object):
def __init__(self,type='House',sqft=None):
self.type = type
self.sqft=sqft
def rsetattr(obj, attr, val):
pre, _, post = attr.rpartition('.')
return setattr(rgetattr(obj, pre) if pre else obj, post, val)
def rgetattr(obj, attr, *args):
def _getattr(obj, attr):
return getattr(obj, attr, *args)
return functools.reduce(_getattr, [obj] + attr.split('.'))
if __name__=='__main__':
p = Person()
print(rgetattr(p, 'pet.favorite.color', 'calico'))
# 'calico'
try:
# Without a default argument, `rgetattr`, like `getattr`, raises
# AttributeError when the dotted attribute is missing
print(rgetattr(p, 'pet.favorite.color'))
except AttributeError as err:
print(err)
# 'Pet' object has no attribute 'favorite'
rsetattr(p, 'pet.name', 'Sparky')
rsetattr(p, 'residence.type', 'Apartment')
print(p.__dict__)
print(p.pet.name)
# Sparky
print(p.residence.type)
# Apartment
For an out of the box solution, you can use operator.attrgetter:
from operator import attrgetter
attrgetter(dotted_path)(obj)
For one parent and one child:
if __name__=='__main__':
p = Person()
parent, child = 'pet.name'.split('.')
setattr(getattr(p, parent), child, 'Sparky')
parent, child = 'residence.type'.split('.')
setattr(getattr(p, parent), child, 'Sparky')
print p.__dict__
This is simpler than the other answers for this particular use case.
unutbu's answer (https://stackoverflow.com/a/31174427/2683842) has a "bug". After getattr() fails and is replaced by default, it continues calling getattr on default.
Example: rgetattr(object(), "nothing.imag", 1) should equal 1 in my opinion, but it returns 0:
getattr(object(), 'nothing', 1) == 1.
getattr(1, 'imag', 1) == 0 (since 1 is real and has no complex component).
Solution
I modified rgetattr to return default at the first missing attribute:
import functools
DELIMITER = "."
def rgetattr(obj, path: str, *default):
"""
:param obj: Object
:param path: 'attr1.attr2.etc'
:param default: Optional default value, at any point in the path
:return: obj.attr1.attr2.etc
"""
attrs = path.split(DELIMITER)
try:
return functools.reduce(getattr, attrs, obj)
except AttributeError:
if default:
return default[0]
raise
This should be a
def getNestedAttr(obj,nestedParam):
next = obj
for p in nestedParam.split('.'):
next = getattr(next,p)
return next
class Issue : pass
issue = Issue()
issue.status = Issue()
issue.status.name = "Hello"
getattr(issue,'status.name')
'''
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Issue' object has no attribute 'status.name'
'''
getNestedAttr(issue,'status.name')
#'Hello'
simple solution
I made a simple version based on ubntu's answer called magicattr that also works on attrs, lists, and dicts by parsing and walking the ast.
For example, with this class:
class Person:
settings = {
'autosave': True,
'style': {
'height': 30,
'width': 200
},
'themes': ['light', 'dark']
}
def __init__(self, name, age, friends):
self.name = name
self.age = age
self.friends = friends
bob = Person(name="Bob", age=31, friends=[])
jill = Person(name="Jill", age=29, friends=[bob])
jack = Person(name="Jack", age=28, friends=[bob, jill])
You can do this
# Nothing new
assert magicattr.get(bob, 'age') == 31
# Lists
assert magicattr.get(jill, 'friends[0].name') == 'Bob'
assert magicattr.get(jack, 'friends[-1].age') == 29
# Dict lookups
assert magicattr.get(jack, 'settings["style"]["width"]') == 200
# Combination of lookups
assert magicattr.get(jack, 'settings["themes"][-2]') == 'light'
assert magicattr.get(jack, 'friends[-1].settings["themes"][1]') == 'dark'
# Setattr
magicattr.set(bob, 'settings["style"]["width"]', 400)
assert magicattr.get(bob, 'settings["style"]["width"]') == 400
# Nested objects
magicattr.set(bob, 'friends', [jack, jill])
assert magicattr.get(jack, 'friends[0].friends[0]') == jack
magicattr.set(jill, 'friends[0].age', 32)
assert bob.age == 32
It also won't let you/someone call functions or assign a value since it doesn't use eval or allow Assign/Call nodes.
with pytest.raises(ValueError) as e:
magicattr.get(bob, 'friends = [1,1]')
# Nice try, function calls are not allowed
with pytest.raises(ValueError):
magicattr.get(bob, 'friends.pop(0)')
And a easy to understand three-liner based on jimbo1qaz's answer, reduced to the very limit:
def rgetattr(obj, path, default):
try:
return functools.reduce(getattr, path.split(), obj)
except AttributeError:
return default
Usage:
>>> class O(object):
... pass
... o = O()
... o.first = O()
... o.first.second = O()
... o.first.second.third = 42
... rgetattr(o, 'first second third', None)
42
Just keep in mind that "space" is not a typical delimiter for this use case.
Thanks for the accepted answer above. It was helpful.
In case anyone wants to extend the use for hasattr use the code below:
def rhasattr(obj, attr):
_nested_attrs = attr.split(".")
_curr_obj = obj
for _a in _nested_attrs[:-1]:
if hasattr(_curr_obj, _a):
_curr_obj = getattr(_curr_obj, _a)
else:
return False
return hasattr(_curr_obj, _nested_attrs[-1])
Ok so while typing the question I had an idea of how to do this and it seems to work fine. Here is what I came up with:
def set_attribute(obj, path_string, new_value):
parts = path_string.split('.')
final_attribute_index = len(parts)-1
current_attribute = obj
i = 0
for part in parts:
new_attr = getattr(current_attribute, part, None)
if current_attribute is None:
print 'Error %s not found in %s' % (part, current_attribute)
break
if i == final_attribute_index:
setattr(current_attribute, part, new_value)
current_attribute = new_attr
i+=1
def get_attribute(obj, path_string):
parts = path_string.split('.')
final_attribute_index = len(parts)-1
current_attribute = obj
i = 0
for part in parts:
new_attr = getattr(current_attribute, part, None)
if current_attribute is None:
print 'Error %s not found in %s' % (part, current_attribute)
return None
if i == final_attribute_index:
return getattr(current_attribute, part)
current_attribute = new_attr
i += 1
I guess this solves my question, but I am still curious if there is a better way to do this?
I feel like this has to be something pretty common in OOP and python, so I'm surprised gatattr and setattr do not support this natively.
Here's something similar to ChaimG's answer, but it works with an arbitrary number of cases. However, it only supports get attributes, not setting them.
requested_attr = 'pet.name'
parent = Person()
sub_names = requested_attr.split('.')
sub = None
for sub_name in sub_names:
try:
sub = parent.__getattribute__(sub_name)
parent = sub
except AttributeError:
raise Exception("The panel doesn't have an attribute that matches your request!")
pets_name = sub
I just love recursive functions
def rgetattr(obj,attr):
_this_func = rgetattr
sp = attr.split('.',1)
if len(sp)==1:
l,r = sp[0],''
else:
l,r = sp
obj = getattr(obj,l)
if r:
obj = _this_func(obj,r)
return obj
I know this post is pretty old but below code might help some one.
def getNestedObjectValue(obj={}, attr=""):
splittedFields = attr.split(".")
nestedValue = ""
previousValue = ""
for field in splittedFields:
previousValue = nestedValue
nestedValue = (
obj.get(field) if previousValue == "" else previousValue.get(field)
)
return nestedValue
print(
getNestedObjectValue(
obj={
"name": "ADASDASD",
"properties": {"somefield": {"value": "zxczxcxczxcxzc"}},
},
attr="properties.somefield.value",
)
)
Output
PS C:\myprograms\samples> python .\sample.py
zxczxcxczxcxzc