Guess number, no such attribute, python 3 - oop

I'm learning OOP in python and was trying to run this small game in OOP style, but for some reason system doesn't find object's attributes.
Here's the problem:
Traceback (most recent call last):
File "HelloUsername.py", line 47, in <module>
newGameGTN = GuessTheNumber()
File "HelloUsername.py", line 6, in __init__
self.start_game()
File "HelloUsername.py", line 32, in start_game
player = player_choice()
NameError: name 'player_choice' is not defined
On this code in python 3:
from random import randint
class GuessTheNumber(object):
"""docstring for GuessTheNumber"""
def __init__(self):
self.start_game()
self.player_choice()
self.compare_numbers()
def player_choice(self):
choice = int(input("Choose your number: "))
if choice in range(101):
return(choice)
else:
print("Please enter a number 0-100")
player_choice()
def compare_numbers(self, computer, player):
if player == computer:
return(0)
elif player > computer:
return(1)
elif player < computer:
return(-1)
def start_game(self):
computer = randint(0, 100)
turn = 0
for turn in range(3):
player = player_choice()
x = compare_numbers(computer, player)
print(computer)
if x == -1:
print("too small")
elif x == 1:
print("too big")
elif x == 0:
print("you win")
break
turn += 1
print("game over")
newGameGTN = GuessTheNumber()
newGameGTN.start_game()

NameError is not the same as AttributeError (which you mention in the question's summary). A NameError exception means that the name referenced in your code does not exist. A name can be a local variable, or a variable in an enclosing scope.
All methods in a class need to be called on an instance of that class. (staticmethods and classmethods not withstanding) Instead of name = player_choice() you need to write name = self.player_choice(). Likewise for all other occurrences where you call a method defined in the class.

Related

linked stack sum all

I am currently working through a problem where I have to implement a linked stack and write a method to compute the sum of its elements but my code will not work properly for the sum_all method. Any help is much appreciated
class Node:
def __init__(self, item = None, link = None):
"""creates a node with a given item and link"""
self.item = item
self.link = link
class LinkedStack:
def __init__(self):
self.top = None
self.stack_size = 0
def size(self):
return self.stack_size
def is_empty(self):
return self.stack_size == 0
def is_full(self):
# linked stacks are never full
return False
def push(self, item):
"""Places the given item at the top of the stack
if there is capacity, or raises an Exception."""
new_node = Node(item, self.top)
self.top = new_node
self.stack_size += 1
def pop(self):
"""Removes and returns the top element of the stack,
or raises an Exception if there is none."""
if self.is_empty():
raise Exception("The stack is empty")
item = self.top.item # get a reference to the top item before we remove the link to it
self.top = self.top.link
self.stack_size -= 1
return item
def sum_all(self):
current = self.top
sum = 0
while current is not None:
sum = sum + current.item
current = current.link
return sum
my_linked_stack = LinkedStack()
n1 = Node(1)
print(n1.item)
n2 = Node(2, n1)
print(n2.item, n2.link.item)
n3 = Node(3, n2)
print(n3.item, n3.link.item, n3.link.link.item)
my_linked_stack.push(n1)
my_linked_stack.push(n2)
my_linked_stack.push(n3)
print(my_linked_stack.sum_all()
returns
1
(2, 1)
(3, 2, 1)
Traceback (most recent call last):
File "/Applications/PyCharm CE.app/Contents/plugins/python-ce/helpers/pydev/pydevd.py", line 1496, in _exec
pydev_imports.execfile(file, globals, locals) # execute the script
File "/Users/evanchauhan/Desktop/technical interview lecture resources/2085 resources/Stacks and Queues implemented with arrays/linked_queues.py", line 106, in <module>
print(my_linked_stack.sum_all())
File "/Users/evanchauhan/Desktop/technical interview lecture resources/2085 resources/Stacks and Queues implemented with arrays/linked_queues.py", line 89, in sum_all
sum = sum + current.item
TypeError: unsupported operand type(s) for +: 'int' and 'instance'
I am trying to write a function named sum_all(self) that will compute the sum of all elements in my linked stack but I keep encountering this error which im not sure how to resolve

How to fix when Python says: UnboundLocalError: local variable 'money' referenced before assignment

I am having troubles with my money variable. Each time I save and run, a ERROR pops up and I try to fix the problem but can't figure out how? I really need a reply so please soon. This is my project:
money = 0.0
Chop = 0
spike = 0
name = input("What is your name?")
greeting = 'Hello ' + name + ','
def Command():
Command = input('Press \"C\" to continue or Press \"E\" to exit -->')
if name == 'SpikeTheKing':
print('Welcome Spike,')
spike = 1
elif name == 'Spike':
spike = 1
print ('Welcome Spike,')
else:
print(greeting)
def draw_line():
print ('----------------')
def Work1():
Chop = input('Type anything to earn $1 -->')
while Chop == ('C') or ('c'):
print ('$1 earned')
money = money+1
displayMoney()
Chop = 0
Command()
def displayMoney():
draw_line()
print(('Money = '),('$'),(money))
draw_line()
displayMoney()
Work1()
if Command == ('C'):
Work1()
if Command == ('E'):
quit()
Each time I save and run this happens:
Traceback (most recent call last):
File "C:\Users\Leora\Desktop\Python Files\Python.py", line 35, in <module>
Work1()
File "C:\Users\Leora\Desktop\Python Files\Python.py", line 26, in Work1
money = money+1
UnboundLocalError: local variable 'money' referenced before assignment
What should I change?
In the def Work code, before you do money = money+1, you will need to initialize money. So in the first statement after def Work, do: money = 0...
Alternatively, money is global , so you could do global money, in the first line after def Work instead of money=0....
The global keyword tells the interpreter, that money is defined outside the scope of Work function and to look for it there

Can I restrict objects in Python3 so that only attributes that I make a setter for are allowed?

I have something called a Node. Both Definition and Theorem are a type of node, but only Definitions should be allowed to have a plural attribute:
class Definition(Node):
def __init__(self,dic):
self.type = "definition"
super(Definition, self).__init__(dic)
self.plural = move_attribute(dic, {'plural', 'pl'}, strict=False)
#property
def plural(self):
return self._plural
#plural.setter
def plural(self, new_plural):
if new_plural is None:
self._plural = None
else:
clean_plural = check_type_and_clean(new_plural, str)
assert dunderscore_count(clean_plural)>=2
self._plural = clean_plural
class Theorem(Node):
def __init__(self, dic):
self.type = "theorem"
super().__init__(dic)
self.proofs = move_attribute(dic, {'proofs', 'proof'}, strict=False)
# theorems CANNOT have plurals:
# if 'plural' in self:
# raise KeyError('Theorems cannot have plurals.')
As you can see, Definitions have a plural.setter, but theorems do not. However, the code
theorem = Theorem(some input)
theorem.plural = "some plural"
runs just fine and raises no errors. But I want it to raise an error. As you can see, I tried to check for plurals manually at the bottom of my code shown, but this would only be a patch. I would like to block the setting of ANY attribute that is not expressly defined. What is the best practice for this sort of thing?
I am looking for an answer that satisfies the "chicken" requirement:
I do not think this solves my issue. In both of your solutions, I can
append the code t.chicken = 'hi'; print(t.chicken), and it prints hi
without error. I do not want users to be able to make up new
attributes like chicken.
The short answer is "Yes, you can."
The follow-up question is "Why?" One of the strengths of Python is the remarkable dynamism, and by restricting that ability you are actually making your class less useful (but see edit at bottom).
However, there are good reasons to be restrictive, and if you do choose to go down that route you will need to modify your __setattr__ method:
def __setattr__(self, name, value):
if name not in ('my', 'attribute', 'names',):
raise AttributeError('attribute %s not allowed' % name)
else:
super().__setattr__(name, value)
There is no need to mess with __getattr__ nor __getattribute__ since they will not return an attribute that doesn't exist.
Here is your code, slightly modified -- I added the __setattr__ method to Node, and added an _allowed_attributes to Definition and Theorem.
class Node:
def __setattr__(self, name, value):
if name not in self._allowed_attributes:
raise AttributeError('attribute %s does not and cannot exist' % name)
super().__setattr__(name, value)
class Definition(Node):
_allowed_attributes = '_plural', 'type'
def __init__(self,dic):
self.type = "definition"
super().__init__(dic)
self.plural = move_attribute(dic, {'plural', 'pl'}, strict=False)
#property
def plural(self):
return self._plural
#plural.setter
def plural(self, new_plural):
if new_plural is None:
self._plural = None
else:
clean_plural = check_type_and_clean(new_plural, str)
assert dunderscore_count(clean_plural)>=2
self._plural = clean_plural
class Theorem(Node):
_allowed_attributes = 'type', 'proofs'
def __init__(self, dic):
self.type = "theorem"
super().__init__(dic)
self.proofs = move_attribute(dic, {'proofs', 'proof'}, strict=False)
In use it looks like this:
>>> theorem = Theorem(...)
>>> theorem.plural = 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __setattr__
AttributeError: attribute plural does not and cannot exist
edit
Having thought about this some more, I think a good compromise for what you want, and to actually answer the part of your question about restricting allowed changes to setters only, would be to:
use a metaclass to inspect the class at creation time and dynamically build the _allowed_attributes tuple
modify the __setattr__ of Node to always allow modification/creation of attributes with at least one leading _
This gives you some protection against both misspellings and creation of attributes you don't want, while still allowing programmers to work around or enhance the classes for their own needs.
Okay, the new meta class looks like:
class NodeMeta(type):
def __new__(metacls, cls, bases, classdict):
node_cls = super().__new__(metacls, cls, bases, classdict)
allowed_attributes = []
for base in (node_cls, ) + bases:
for name, obj in base.__dict__.items():
if isinstance(obj, property) and hasattr(obj, '__fset__'):
allowed_attributes.append(name)
node_cls._allowed_attributes = tuple(allowed_attributes)
return node_cls
The Node class has two adjustments: include the NodeMeta metaclass and adjust __setattr__ to only block non-underscore leading attributes:
class Node(metaclass=NodeMeta):
def __init__(self, dic):
self._dic = dic
def __setattr__(self, name, value):
if not name[0] == '_' and name not in self._allowed_attributes:
raise AttributeError('attribute %s does not and cannot exist' % name)
super().__setattr__(name, value)
Finally, the Node subclasses Theorem and Definition have the type attribute moved into the class namespace so there is no issue with setting them -- and as a side note, type is a bad name as it is also a built-in function -- maybe node_type instead?
class Definition(Node):
type = "definition"
...
class Theorem(Node):
type = "theorem"
...
As a final note: even this method is not immune to somebody actually adding or changing attributes, as object.__setattr__(theorum_instance, 'an_attr', 99) can still be used -- or (even simpler) the _allowed_attributes can be modified; however, if somebody is going to all that work they hopefully know what they are doing... and if not, they own all the pieces. ;)
You can check for the attribute everytime you access it.
class Theorem(Node):
...
def __getattribute__(self, name):
if name not in ["allowed", "attribute", "names"]:
raise MyException("attribute "+name+" not allowed")
else:
return self.__dict__[name]
def __setattr__(self, name, value):
if name not in ["allowed", "attribute", "names"]:
raise MyException("attribute "+name+" not allowed")
else:
self.__dict__[name] = value
You can build the allowed method list dynamically as a side effect of a decorator:
allowed_attrs = []
def allowed(f):
allowed_attrs.append(f.__name__)
return f
You would also need to add non method attributes manually.
If you really want to prevent all other dynamic attributes. I assume there's a well-defined time window that you want to allow adding attributes.
Below I allow it until object initialisation is finished. (you can control it with allow_dynamic_attribute variable.
class A:
def __init__(self):
self.allow_dynamic_attribute = True
self.abc = "hello"
self._plural = None # need to give default value
# A.__setattr__ = types.MethodType(__setattr__, A)
self.allow_dynamic_attribute = False
def __setattr__(self, name, value):
if hasattr(self, 'allow_dynamic_attribute'):
if not self.allow_dynamic_attribute:
if not hasattr(self, name):
raise Exception
super().__setattr__(name, value)
#property
def plural(self):
return self._plural
#plural.setter
def plural(self, new_plural):
self._plural = new_plural
a = A()
print(a.abc) # fine
a.plural = "yes" # fine
print(a.plural) # fine
a.dkk = "bed" # raise exception
Or it can be more compact this way, I couldn't figure out how MethodType + super can get along together.
import types
def __setattr__(self, name, value):
if not hasattr(self, name):
raise Exception
else:
super().__setattr__(name,value) # this doesn't work for reason I don't know
class A:
def __init__(self):
self.foo = "hello"
# after this point, there's no more setattr for you
A.__setattr__ = types.MethodType(__setattr__, A)
a = A()
print(a.foo) # fine
a.bar = "bed" # raise exception
Yes, you can create private members that cannot be modified from outside the class. The variable name should start with two underscores:
class Test(object):
def __init__(self, t):
self.__t = t
def __str__(self):
return str(self.__t)
t = Test(2)
print(t) # prints 2
t.__t = 3
print(t) # prints 2
That said, trying to access such a variable as we do in t.__t = 3 will not raise an exception.
A different approach which you can take to achieve the wanted behavior is using functions. This approach will require "accessing attributes" using functional notation, but if that doesn't bother you, you can get exactly what you want. The following demo "hardcodes" the values, but obviously you can have Theorem() accept an argument and use it to set values to the attributes dynamically.
Demo:
# -*- coding: utf-8 -*-
def Theorem():
def f(attrib):
def proofs():
return ''
def plural():
return '◊◊◊◊◊◊◊◊'
if attrib == 'proofs':
return proofs()
elif attrib == 'plural':
return plural()
else:
raise ValueError("Attribute [{}] doesn't exist".format(attrib))
return f
t = Theorem()
print(t('proofs'))
print(t('plural'))
print(t('wait_for_error'))
OUTPUT

◊◊◊◊◊◊◊◊
Traceback (most recent call last):
File "/Users/alfasi/Desktop/1.py", line 40, in <module>
print(t('wait_for_error'))
File "/Users/alfasi/Desktop/1.py", line 32, in f
raise ValueError("Attribute [{}] doesn't exist".format(attrib))
ValueError: Attribute [wait_for_error] doesn't exist

Pygame gives me TypeError: add() argument after * must be a sequence, not Ball when adding Ball to sprite group

Recently, I have been messing around with pygame and I decided to make a pong clone. However, I am running into problems with the ball class.
This is my class:
class Ball(pygame.sprite.Sprite):
""" This class represents the ball that bounces around. """
# Constructor function
def __init__(self, x, y):
# Call the parent's constructor
pygame.sprite.Sprite().__init__(self)
# Set height, width
self.image = pygame.Surface([15, 15])
self.image.fill(white)
# Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.rect.y = y
self.rect.x = x
# Set speed vector
self.change_x = 0
self.change_y = 0
def goal(self):
if self.rect.x <= SCREEN_WIDTH:
playerscore =+ 1
print playerscore
elif self.rect.x >= 0:
aiscore =+ 1
print aiscore
def update(self):
""" Update the ball's position. """
# Get the old position, in case we need to go back to it
old_x = self.rect.x
new_x = old_x + self.change_x
self.rect.x = new_x
# Did this update cause us to hit a wall?
collide = pygame.sprite.spritecollide(self, allsprites_list, False)
if collide:
# Whoops, hit a wall. Go back to the old position
self.rect.x = old_x
self.change_x *= -1
old_y = self.rect.y
new_y = old_y + self.change_y
self.rect.y = new_y
# Did this update cause us to hit a wall?
collide = pygame.sprite.spritecollide(self, allsprites_list, False)
if collide:
# Whoops, hit a wall. Go back to the old position
self.rect.y = old_y
self.change_y *= -1
if self.rect.x < -20 or self.rect.x > screen_width + 20:
self.change_x = 0
self.change_y = 0
This adds the ball to a sprite group:
self.ball = Ball(100, 250)
self.all_sprites_list.add(self.ball)
And this is the traceback:
Traceback (most recent call last):
File "C:/Users/Enigma/Desktop/pong.py", line 312, in <module>
main()
File "C:/Users/Enigma/Desktop/pong.py", line 290, in main
game = Game()
File "C:/Users/Enigma/Desktop/pong.py", line 218, in __init__
self.ball = Ball(100, 250)
File "C:/Users/Enigma/Desktop/pong.py", line 83, in __init__
pygame.sprite.Sprite().__init__(self)
File "C:\Python27\lib\site-packages\pygame\sprite.py", line 114, in __init__
if groups: self.add(groups)
File "C:\Python27\lib\site-packages\pygame\sprite.py", line 129, in add
else: self.add(*group)
File "C:\Python27\lib\site-packages\pygame\sprite.py", line 129, in add
else: self.add(*group)
TypeError: add() argument after * must be a sequence, not Ball
I have searched the web and all of the posts that I could find here at SO, however none of the seem to apply to this particular conundrum. Any and all help would be appreciated.
I am running python 2.7.9 on Windows 7.
This line:
pygame.sprite.Sprite().__init__(self)
is almost certainly wrong. You want to call the method on the class, not an instance.
pygame.sprite.Sprite.__init__(self)

Learning Python 3.3 - TypeError: 'int' object is not callable

I was trying to solve Challenge 2 at the end of the classes chapter (Chapter 8) in "Python Programming for the Absolute Beginner" which is stated as:
Write a program that simulates a television by creating it as an object. The user should be able to enter a channel number and raise or lower the volume. Make sure that the channel number and the volume level stay within valid ranges.
I keep getting: TypeError: 'int' object is not callable, which at this stage just isn't very helpful.
I'm a beginner but I've seen something really similar working (see at the bottom right below my code) and nearly went as far as nearly copying that code. Could somebody maybe explain what's wrong with this and how I can get it to work?
Here's the complete error:
Traceback (most recent call last):
File "Untitled 3.py", line 59, in <module>
main()
File "Untitled 3.py", line 50, in main
tv.channel(newchannel = int(input("What channel would you like to set the TV to?")))
TypeError: 'int' object is not callable
My code is below,
Thanks
class Television(object):
"""a TV"""
def __init__(self, channel = 0, volume = 0):
self.channel = channel
self.volume = volume
def channel(self, newchannel = 0):
if newchannel <= 0 or newchannel >9:
print("No negative numbers or numbers higher than 9. Start again from the menu")
else:
self.channel = newchannel
print("You set the TV on channel", self.channel)
def volume(self, newvolume = 0):
if newvolume <= 0 or newvolume >9:
print("No negative numbers or numbers higher than 9. Start again from the menu")
else:
self.volume = newvolume
print("You set the TV on volume", self.volume)
def watch(self):
print("You are watching channel", self.channel, "at volume", self.volume)
def main():
tv = Television()
choice = None
while choice != "0":
print \
("""
TV
0 - Quit
1 - Watch the TV
2 - Change channel
3 - Set the volume
""")
choice = input("Choice: ")
print()
# exit
if choice == "0":
print("Good-bye.")
elif choice == "1":
tv.watching()
elif choice == "2":
tv.channel(newchannel = int(input("What channel would you like to set the TV to?")))
elif choice == "3":
tv.volume(newvolume = int(input("What channel would you like to set the TV to?")))
# some unknown choice
else:
print("\nSorry, but", choice, "isn't a valid choice.")
main()
("\n\nPress the enter key to exit.")
Why does the following work instead? To me, it looks pretty similar to what I've done.
class Critter(object):
"""A virtual pet"""
def __init__(self, hunger = 0, boredom = 0):
self.hunger = hunger
self.boredom = boredom
def eat(self, food = 4):
print("Brruppp. Thank you.")
self.hunger += food
if self.hunger < 0:
self.hunger = 0
crit = Critter()
print(crit.hunger)
crit.eat(food = int(input("how much do you want to feed him?")))
print(crit.hunger)
The problem is you are defining a method with the same name as a property. That is, you're saying Television.channel is an int, but later you are binding a method to that name.