Using method as a dictionary value - python

class Inventory(object):
def __init__(self):
self.inventory = {
'cash': 500,
'paycheck': 1,
'savings': 1000,
'current_car': 0,
'possible_cars': ['Chevy', 'Honda', 'BMW'],
'car_cost': [0, 100, 200],
'current_house': 0,
'possible_houses': ['apartment','townhouse','suite'],
'house_cost': [0, 150, 300],
'status': self.status()
}
def status(self):
while self.inventory['cash'] + self.inventory['savings'] > 0:
return True
I'm currently working through "Learn Python the Hard Way", on Exercise 45. I've created a class to list the items relevant to my game, I've stored these keys and values in a dictionary under my init method. Where I'm having trouble is within my last key and it's value - the 'status' key.
What I want for this value to do is reference my status method, which I've set to return true as long as my player has a positive sum of money (other portions of my game will reference .inventory['status'] to check its truth before they execute. Now I've done quick two line proof-of-concept codes to verify that it's possible to use a function as a value - where I'm getting hung up on is how to implement this within a class, specifically when my dictionary is within init.
My error:
Traceback (most recent call last):
File "ex45file1.py", line 151, in <module>
my_inv = Inventory() #TEST
File "ex45file1.py", line 80, in __init__
'status': status()
NameError: global name 'status' is not defined
Where am I going wrong here?

First, that isn't the error that your code produces. In your version you have 'status': status() but on SO you wrote 'status': self.status(). In any case, if you fix that you still have a problem,
AttributeError: 'Inventory' object has no attribute 'inventory'
The reason you get that error is because Python is in the process of defining your inventory attribute yet you are call status which must refer to inventory to give a return value.
You don't even want to be calling the function and saving the return value in the dictionary since that won't allow you to use it dynamically. You should change it such that you don't invoke but just save the reference.
class Inventory(object):
def __init__(self):
self.inventory = {
'cash': 500,
'paycheck': 1,
'savings': 1000,
'current_car': 0,
'possible_cars': ['Chevy', 'Honda', 'BMW'],
'car_cost': [0, 100, 200],
'current_house': 0,
'possible_houses': ['apartment','townhouse','suite'],
'house_cost': [0, 150, 300],
'status': self.status # <--- don't use parens ()
}
And just call the method like,
>>> my_inventory = Inventory()
>>> my_inventory.inventory['status']()
True

I got a different error, but I believe the solution would be the same:
class Inventory(object):
def __init__(self):
self.inventory = {
'cash': 500,
'paycheck': 1,
'savings': 1000,
'current_car': 0,
'possible_cars': ['Chevy', 'Honda', 'BMW'],
'car_cost': [0, 100, 200],
'current_house': 0,
'possible_houses': ['apartment','townhouse','suite'],
'house_cost': [0, 150, 300],
}
self.inventory['status'] = self.status()
def status(self):
while self.inventory['cash'] + self.inventory['savings'] > 0:
return True
My error was complaining about inventory not being defined in status().

I tried copying & pasting your code into my Python interpreter and I get a different error:
>>> inv = new Inventory()
File "<stdin>", line 1
inv = new Inventory()
^
SyntaxError: invalid syntax
>>> inv = Inventory()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 13, in __init__
File "<stdin>", line 16, in status
AttributeError: 'Inventory' object has no attribute 'inventory'
The problem is that you have a cyclic dependency here between inventory and status. You use status to define inventory, but status needs to read inventory before it can return... See the problem?

Related

Python KeyError after returning the value (also error_id 502)

I'm using stackoverflow's API and want to use the 'quota_remaining' so I can perform pagination.
But when I try to print the 'quota_remaining' I am getting a KeyError after the print. So it prints the value but I am not able to store it in a variable because it throws a KeyError afterwards.
This is my code:
# Get data
users_url = 'https://api.stackexchange.com/2.3/users?page=1&pagesize=100&order=desc&sort=modified&site=stackoverflow&filter=!56ApJn82ELRG*IWQxo6.gXu9qS90qXxNmY8e9b'
# Make the API call
response = requests.get(users_url)
result = response.json()
print(result)
print(result['quota_remaining']) # line 33
quota_remaining = result['quota_remaining']
And this is what is returned (I included a sample of the print(result)):
{'items': ['badge_counts': {'bronze': 6, 'silver': 0, 'gold': 0}, 'view_count': 21, 'answer_count': 2, 'question_count': 14, 'reputation_change_quarter': 0, 'reputation': 75, 'user_id': 2498916, 'link': 'https://stackoverflow.com/users/2498916/oscar-salas', 'display_name': 'Oscar Salas'}], 'has_more': True, 'backoff': 10, 'quota_max': 300, 'quota_remaining': 261, 'page': 1, 'page_size': 100}
261
1
{'error_id': 502, 'error_message': 'Violation of backoff parameter', 'error_name': 'throttle_violation'}
Traceback (most recent call last):
File "test.py", line 33, in <module>
print(result['quota_remaining'])
KeyError: 'quota_remaining'
I also don't understand why I am getting the error 502, what am I violating? What is the backoff parameter?
Change variable name, try this:
users_url = 'https://api.stackexchange.com/2.3/users?page=1&pagesize=100&order=desc&sort=modified&site=stackoverflow&filter=!56ApJn82ELRG*IWQxo6.gXu9qS90qXxNmY8e9b'
# Make the API call
response = requests.get(users_url)
result = response.json()
print(result)
print(result['quota_remaining']) # line 33
quota_remaining1 = result['quota_remaining']
Okay I've figured it out. I was making too many requests as #SimonT pointed out.
The backoff parameter meant I had to wait that many seconds before hitting the same method again. I'm my case I had a backoff = 10 so I set up a time.sleep(10) between requests.
This is actually my full code (I had only a sample in the question as I did not understood the keyError was actually because of the throttle violation - a rookie mistake):
while quota > 240:
# Get data
users_url = f'https://api.stackexchange.com/2.3/users?page={page}&pagesize=100&order=desc&sort=modified&site=stackoverflow&filter=!56ApJn82ELRG*IWQxo6.gXu9qS90qXxNmY8e9b'
# Make the API call
response = requests.get(users_url)
result = response.json()
print(result['quota_remaining'])
print(result['page'])
# Update counters
quota = result['quota_remaining']
page = result['page']
# Save users in a list
for user in result["items"]:
user['_id'] = user.pop('user_id')
results.append(user)
if result['has_more'] == True:
time.sleep(10)
page += 1
else:
break

Why does my PyCharm console not run my function?

def is_table(tab):
if len(tab) != 3:
return False
valid = (-1, 0, 1)
for a in range(0, 3):
if len(tab[a]) != 3:
return False
for b in range(0, 3):
if tab[a][b] not in valid:
return False
return True
When I try to run is_table(((0,0,0),(0,0,0),(0,0,0))) on console, I get this error:
Traceback (most recent call last):
File "<input>", line 1, in <module>
NameError: name 'is_table' is not defined
Can anyone explain why? My function is clearly defined, but it still doesn't run on console.
The python console dont know about your files functions. First, edit your file name, removing any space, e.g., jogo_do_galo.py (or another name, like jogo_do_mengao).
Open the python console, and try:
>>> from jogo_do_galo import *
>>> tab = ((True, 0, 0), (0, 0, 0), (0, 0, 0))
>>> eh_tabuleiro(tab)
This will work.
Did you define the function inside the console, or in a separate file?
If you defined the function in a separate file, you'll need to run the file before the function will be recognized by the console.
Found an answer. Right-clicked on file name, settings, run on console.

How can I avoid dynamically generated classes derived from metaclass not to end up as the same class?

What I'm trying to do is to perform hundred of unit tests to a function, which I can derive from a dictionary. Unfortunately I cannot use any of the existing packages for parametrized tests (like nose), so I'm trying to get my own solution.
My intention with the following example code is to create 3 classes (one for each test) that will exist in the global scope so that unittests can pick it up and run the corresponding tests.
tests = [
{'text': 'text1fdskla3fsda4',
'result': [1, 3, 4],
},
{'text': 'fdsg45tg5b',
'result': [4, 5, 5,5 ],
},
{'text': 'fsddf4',
'result': [4, 2],
}
]
def evaluate(text):
out = []
for char in text:
if char.isdigit():
out.append(int(char))
return out
class TestMeta(type):
def __new__(cls, name, bases, attrs):
name = str(test['text'])
return type.__new__(cls, name, (unittest.TestCase,), attrs)
for test in tests:
class T(object):
__metaclass__ = TestMeta
def testOne(self):
self.assertEqual(test['result'], evaluate(test['text']))
globals()[(test['text'])] = copy.deepcopy(T)
unittest.main()
When I run the above code, I get four unittests, which is one more than expected, but on top of that, instead of having the output of each unittest, it seems like the class I'm creating is always the same (even if I'm actually setting a different name and parameters to each):
======================================================================
FAIL: testOne (__main__.fsddf4)
----------------------------------------------------------------------
Traceback (most recent call last):
File "ble.py", line 45, in testOne
self.assertEqual(test['result'], evaluate(test['text']))
AssertionError: [4, 2] != [4]
======================================================================
FAIL: testOne (__main__.fdsg45tg5b)
----------------------------------------------------------------------
Traceback (most recent call last):
File "ble.py", line 45, in testOne
self.assertEqual(test['result'], evaluate(test['text']))
AssertionError: [4, 2] != [4]
======================================================================
FAIL: testOne (__main__.fsddf4)
----------------------------------------------------------------------
Traceback (most recent call last):
File "ble.py", line 45, in testOne
self.assertEqual(test['result'], evaluate(test['text']))
AssertionError: [4, 2] != [4]
======================================================================
FAIL: testOne (__main__.text1fdskla3fsda4)
----------------------------------------------------------------------
Traceback (most recent call last):
File "ble.py", line 45, in testOne
self.assertEqual(test['result'], evaluate(test['text']))
AssertionError: [4, 2] != [4]
----------------------------------------------------------------------
Ran 4 tests in 0.002s
FAILED (failures=4)
deepcopy is an attempt to get a different output, but didn't help. The explicit creation of the class in the globals dict was due to the fact that simply creating the classes on the for loop just yields one unittest.
Beyond the deep attempt at meta-everything, you're running into a typical Python beginner problem: global name lookups are done at runtime.
So, in your code:
for test in tests:
class T(object):
__metaclass__ = TestMeta
def testOne(self):
self.assertEqual(test['result'], evaluate(test['text']))
when testOne runs, it looks up test in the globals dictionary at that time -- the time it runs -- by which time test is set to the value it was set to most recently, of course.
You need to force the binding to happen earlier, which you can do e.g simply by changing
def testOne(self):
into
def testOne(self, test=test):
This change forces the global test to be looked up at the time def executes (as opposed to the later time when the body of the method executes), which is the same time the class statement executes -- i.e, once per leg of the loop, when global variable test is set to the current item of list tests.

Python: Unable to add a new key:value to a dictionary

I am making a dictionary for storing tests and their grades for different students.
def tests():
test1 = {'craig':88, 'jeanie':100}
test2 = {'craig':85, 'jeanie':95}
test3 = {'craig':80, 'jeanie':98}
return test1,test2,test3
def actions(test1,test2,test3):
test1.update({'john':95})
test1.update({'chris':92})
test1.update({'charles',100})
test2.update({'john':100})
test2.update({'chris':96})
test2.update({'charles',98})
test3.update({'john':97})
test3.update({'chris':100})
test3.update({'charles',94})
return test1,test2,test3
def main():
one,two,three = tests()
one,two,three = actions(one,two,three)
print (test1,test2,test3)
main()
However, when I try to append a new key:value to my dicts two errors come up:
First:
Traceback (most recent call last):
File "C:\file.py", line 26, in <module>
main()
File "C:\file.py", line 24, in main
one,two,three = actions(one,two,three)
File "C:\file.py", line 14, in actions
test1.update({'charles',100})
TypeError: cannot convert dictionary update sequence element #0 to a sequence
Second:
Traceback (most recent call last):
File "C:\file.py", line 26, in <module>
main()
File "C:\file.py", line 24, in main
one,two,three = actions(one,two,three)
File "C:\file.py", line 14, in actions
test1.update({'charles',100})
ValueError: dictionary update sequence element #0 has length 7; 2 is required
If I run it over and over again, sometimes the first error comes up, sometimes the other.
I do not want any imports such as collections.
test1.update({'charles',100})
is updating the dict with a set not a dict, which it clearly cannot use to update ... instead of sets pass it dicts
test1.update({'charles':100})
just to demonstrate
{1,2,3,4,4,5,6} # a set that will contain 1,2,3,4,5,6
{1:2,3:4,4:5} # a dict with 3 elements dict(1=2,3=4,4=5)
If I understand your need you need add new values not update and for that operation you need change update for setdefault method. I have tested on Aptana Studio that code:
def tests():
test1 = {'craig':88, 'jeanie':100}
test2 = {'craig':85, 'jeanie':95}
test3 = {'craig':80, 'jeanie':98}
return test1,test2,test3
def actions(test1,test2,test3):
test1.setdefault('john',95)
test1.setdefault('chris',92)
test1.setdefault('charles',100)
test2.setdefault('john',100)
test2.setdefault('chris',96)
test2.setdefault('charles',98)
test3.setdefault('john',97)
test3.setdefault('chris',100)
test3.setdefault('charles',94)
return test1,test2,test3
def main():
one,two,three = tests()
one,two,three = actions(one,two,three)
print(one,two,three)
main()
and get response:
one - {'john': 95, 'charles': 100, 'jeanie': 100, 'chris': 92, 'craig': 88}
two - {'john': 100, 'charles': 98, 'jeanie': 95, 'chris': 96, 'craig': 85}
three - {'john': 97, 'charles': 94, 'jeanie': 98, 'chris': 100, 'craig': 80}
Your problem is that update search one dictionary with key for update your value and not insert but setdefault insert new pair key:value with that syntax case not exists and return the value for one key case her exists.
Good work for you,
See answer here:
Add new keys to a dictionary?
To update:
#### Inserting/Updating value ####
data['a']=1 # updates if 'a' exists, else adds 'a'
# OR #
data.update({'a':1})
# OR #
data.update(dict(a=1))
# OR #
data.update(a=1)
# OR #
data.update([(a,1)])
Instead of this:
test3.update({'charles',94})

Dispatch dictionary (HomeWork)

def get_map_iterator(slist,gfunc=None):
index = 0
def Next():
nonlocal index
x = slist[index]
index = index + 1
return x
def has_more():
if slist[index] != None :
return True
else:
return False
dispatch = {
'Next': lambda: gfunc(Next()),
'has_more': has_more
}
return dispatch
it = get_map_iterator((1,3,6))
for i in range(i,6):
it['Next']()
it = get_map_iterator((1,3,6),lambda x:1/x)
while it['has_more']():
it['next']()
p.s
the results of of this code should be :
1
3
6
no more items
no more items
1.0
0.33333
0.166666
How does the change to gfunc will affect this, i mean what will i will need to change in order for this to work if i do get a func or i dont get a func
get_map_iterator() returns a function object (dispatch). You are trying to treat that object as if it was a dictionary.
You want to call it instead:
while it('has_more'):
it('Next')
Your dispatch() function does not itself return another function object, so you'd not call whatever it() returns.
Your has_more route then fails with:
>>> it('has_more')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 17, in dispatch
File "<stdin>", line 9, in has_more
TypeError: next expected at least 1 arguments, got 0
presumably because you meant to use the Next() function you defined, not the built-in next() function.
However, even fixing that won't get you your output, because slist[0] != slist[1].
It sounds as if you were trying to actually return a dictionary:
dispatch = {
'Next': lambda: gfunc(Next()),
'has_more': has_more
}
return dispatch
This return value you would use like you did originally, by looking up the callable via a key, then calling it.

Categories

Resources