AttributeError: 'bool' object has no attribute 'keys' - python

i am a newbie on this site and am also a newbie to programming, i'm trying to learn python using a book for beginners on python 3.1. I have come across an example that just won't work whatever I try, I have searched for faults in writing about 10 times still it seems exactly what I see in the book. This is the example:
the fridge class
#!/usr/bin/env
class Fridge:
"""methods:
has(food_name [, quantity])-chk if string is in the fridge
has_various(foods)-chk if enough food is in the fridge
add_one(food_name) -adds 1 food
add_many(food_dict)- adds dict to fridge
get_one(food_name)- take out 1 food
get_many(food_dict) - a dict out of fridge
get_ingred(food)- if passed an obj get the list of __ingredients__
"""
def __init__(self, items={}) :
if type(items) != type({}):
raise typeError("Fridge req a dict but was given %s" % type(items))
self.items=items
return
def __add_multi(self, food_name, quantity):
if (not food_name in self.items):
self.items[food_name]=0
self.items[food_name]=self.items[food_name]+quantity
def add_one(self, food_name):
if type(food_name) != type(""):
raise TypeError ("add_one requires a string givem a %s " % type(food_name))
else:
self.__add_multi(food_name, 1)
return True
def add_many(self, food_dict):
if type(food_dict) != type({}):
raise TypeError ("add_many requires a dict, got a %s" % type(food_dict))
for item in food_dict.keys() :
self.__add_multi(item, food_dict[item])
return
def has(self, food_name, quantity=1):
return self.has_varoius({food_name:quantity})
def has_various(self, foods):
try:
for food in foods.keys():
if self.items[food] < foods[food]:
return False
return True
except KeyError:
return Fasle
def __get_multi(self, food_name, quantity):
try:
if (self.items[food_name] is None) :
return False
if (quantity > self.items[food_name]):
return False
self.items[food_name] = self.items[food_name] - quantity
except KeyError:
return False
return quantity
def get_one(self, food_name):
if type(food_name) !=type(""):
raise TypeError("get_one requires a string and was given a %s" % type(food_name))
else:
result=self.__get_multi(food_name, 1)
return result
def get_many(self, food_dict):
if self.has_various(food_dict):
foods_removed={}
for item in food_dict.keys():
foods_removed[item]=self.__get_multi(item, food_dict[item])
return foods_removed
def get_ingredients(self, food):
try:
ingredients=self.get_many(food.__ingredients())
except AttributeError:
return False
if ingredients!=False:
return ingredients
the omelet class
#!/usr/bin/env python3.3
class Omelet:
def __init__(self, kind="cheese"):
self.set_kind(kind)
return
def __ingredients__(self):
return self.needed_ingredients
def get_kind(self):
return self.kind
def set_kind(self, kind):
possible_ingredients=self.__known_kinds(kind)
if possible_ingredients == False :
return False
else:
self.kind=kind
self.needed_ingredients= possible_ingredients
def set_new_kind(self, name, ingredients):
self.kind=name
self.needed_ingredients= ingredients
return
def __known_kinds(self, kind):
if kind == "cheese":
return {"eggs":2, "milk":1, "cheese":1}
elif kind == "mushroom":
return {"eggs":2, "milk":1, "cheese":1, "mushroom":2}
elif kind == "onion":
return {"eggs":2, "milk":1, "cheese":1, "onion":1}
else:
return False
def get_ingredients(self, fridge):
self.from_fridge= fridge.get_ingredients(self)
def mix(self):
for ingredient in self.from_fridge.keys():
print("mixing %d %s for the %s omelet" % (self.from_fridge["ingredient"], ingredient, self.kind))
self.mixed=True
def make(self):
if self.mixed == True:
print("Cooking the %s omelet!" % self.kind)
self.cooked = True
this is how i invoke the classes and what i do to use the classes and the error i get
>>> exec(open("/home/knoppix/test/fridge.py").read())
>>> exec(open("/home/knoppix/test/omelet.py").read())
>>> o=Omelet("cheese")
>>> f=Fridge({"cheese":5, "milk":4, "eggs":12})
>>> o.get_ingredients(f)
>>> o.mix()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 41, in mix
AttributeError: 'bool' object has no attribute 'keys'
Please excuse me if there is a typing fault in the code , it`s exactly what I found in the book!

def get_ingredients(self, fridge):
self.from_fridge= fridge.get_ingredients(self)
In this function, your fridge.get_ingredients() might be returning False.
So self.from_fridge has Boolean value which does not have keys() method.
You may want to add appropriate check in mix() method.

The function "__known_kinds(kind)" should preferably return {} to be consistent, instead of different object types although "None" is better than "False".
if kind == "cheese":
return {"eggs":2, "milk":1, "cheese":1}
elif kind == "mushroom":
return {"eggs":2, "milk":1, "cheese":1, "mushroom":2}
elif kind == "onion":
return {"eggs":2, "milk":1, "cheese":1, "onion":1}
else:
return {}
Then you only have to deal with dictionary type in mix() method. The else can also be dropped from mix() as exception will be raised if dict == {}.
def mix(self):
if self.from_fridge == {}:
raise IndexError("self.from_fridge returns Nothing")
for ingredient in self.from_fridge.keys():
....

Related

Python __iadd__ TypeError: unsupported operand type(s) for +=

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()?

why am i getting NameError when accessing a function

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)])

Python - __init__() missing 1 required positional argument:

I'm kinda new to python and I can't get past this error:
Traceback (most recent call last):
File "***", line 63, in <module>
bst = Node()
TypeError: __init__() missing 1 required positional argument: 'val'
Basically, the program is a BST which would allow you to insert, search and look for the minimum item by only going left.
Here's the code (sorry, it's hungarian)
class Node:
def __init__(self, val):
self.ertek = val
self.balgyerek = None
self.jobbgyerek = None
self.gyoker = None
def beszur(self, pri):
if self.gyoker:
return self.gyoker.beszur(pri)
else:
self.gyoker = Node(pri)
return True
if self.ertek == pri:
return False
elif self.ertek > pri:
if self.balgyerek:
return self.balgyerek.beszur(pri)
else:
self.balgyerek = Node(pri)
return True
else:
if self.jobbgyerek:
return self.jobbgyerek.beszur(pri)
else:
self.jobbgyerek = Node(pri)
return True
def keres(self, pri):
if self.gyoker:
return self.gyoker.keres(pri)
else:
return False
if(self.ertek == pri):
return True
elif self.ertek > pri:
if self.balgyerek:
return self.balgyerek.keres(pri)
else:
return False
else:
if self.jobbgyerek:
return self.jobbgyerek.keres(pri)
else:
return False
def minimumertek(self):
jelenlegi = self
while(jelenlegi.balgyerek is not None):
jelenlegi = jelenlegi.balgyerek
return self.ertek
bst = Node()
The __init__ method is run as soon as an object of a class is instantiated. Your __init__ method has two positional arguments: self which refers to the object instance and is passed automatically, and val which is assigned to self.ertek. However, you didn't pass the value of val. Hence, the error. Try passing the value of val at the class instantiation. e.g bst = Node('ertek value')

'NoneType' object is not iterable in a class

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.

getattr and setattr on nested subobjects / chained properties?

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

Categories

Resources