Python NameError: name '...' is not defined in a class - python

I'm trying to make a cool little turn based fighting game and one part of my code doesn't like me. I have a menu system that can't determine a few things. (Please keep in mind, I just started classes very recently.)
Making a class entity (for players and enemies).
class Entity:
def __init__(self):
self.name = ''
self.health = 0
self.moveset = []
Making a player class (so I can add more characters) with a menu inside.
class Player(Entity):
options = [menu(),attack(),items(),stats(),flee()]
def menu(self):
menuchoice = input('Menu\n1. Attack\n2. Items\n3. Stats\n4. Flee\n')
if menuchoice not in ['1','2','3','4']:
clear()
options[0]
options[int(menuchoice)]
options[0]
def attack(self):
print('attack')
def items(self):
print('items')
def stats(self):
print('stats')
def flee(self):
print('flee')
Trying to run the menu
player = Player()
player.menu()
Error
Traceback (most recent call last):
File "main.py", line 16, in <module>
class Player(Entity):
File "main.py", line 17, in Player
options = [menu(),attack(),items(),stats(),flee()]
NameError: name 'menu' is not defined
Can someone tell me how to define the menu in this code? I'm using Python 3.6.1.
Edit: Thanks! It works now. I had to add () and move options = [...] to the end!

The variable options is likely intended to maintain a reference to the available options. What it's doing instead is calling the method itself before even the method is defined:
Here's a minimal version that demonstrates the problem (it's on the python interpreter itself:
>>> class Player:
... options = [menu()]
... def menu(self):
... print('menu')
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in Player
NameError: name 'menu' is not defined
>>>
Here's another version without actually calling the method but just adding the reference. It'll hit the same error:
>>> class Player:
... options = [menu]
... def menu(self):
... print('menu')
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in Player
NameError: name 'menu' is not defined
>>>
Here's probably what you want:
>>> class Player:
... options = ['menu']
... def menu(self):
... print('in menu')
...
>>> player = Player()
>>> player
<__main__.Player instance at 0x10a2f6ab8>
>>> player.options
['menu']
>>> player.options[0]
'menu'
>>> type(player.options[0])
<type 'str'>
>>> func = getattr(player, player.options[0])
>>> type(func)
<type 'instancemethod'>
>>> func
<bound method Player.menu of <__main__.Player instance at 0x10a2f6ab8>>
>>> func()
in menu
>>>
Here's the proof if you use it after defining, it won't hit the error - but this isn't the standard/typical usage:
>>> class Player:
... def menu(self):
... print('in menu')
... options = [menu]
...
>>> player = Player()
>>> player.options
[<function menu at 0x10a2f16e0>]
>>> player.options[0]
<function menu at 0x10a2f16e0>
>>> player.options[0]()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: menu() takes exactly 1 argument (0 given)
>>> player.options[0](player)
in menu
>>> Player.options[0](player)
in menu
>>> Player.options[0](player) # calling with class reference
in menu
>>>

Related

Why can't I access an class variable defined in __init__ from outside of the class?

I was wondering about classes and how I can access their values from the outside with either printing it or using the _str__ function. I came across this question:
Python function not accessing class variable
Then I did this test in Shell, but it didn't work as expected. I wonder why the other answer worked, but not this one.
(edit)
My question was answered by how to instantiate a class, not instance variables.
>>> class test:
def __init__(self):
self.testy=0
def __str__(self):
return self.testy
>>> a=test
>>> b=test
>>> print(a)
<class '__main__.test'>
>>> a
<class '__main__.test'>
>>> a.testy
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
a.testy
AttributeError: type object 'test' has no attribute 'testy'
>>>
You had done a mistake while creating objects, please find below differences:
class test:
def __init__(self):
self.testy=0
def __str__(self):
return self.testy
a = test()
b = test()
a.testy
output: 0
What you have done:
c = test
d = test
c.testy
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: type object 'test' has no attribute 'testy'
Explanation:
when you are creating objects for a class use object = class_name()
**https://docs.python.org/3/tutorial/classes.html#class-objects
You define your variable inside init, which is only called when the class is instantiated. For a longer explanation, I'd refer you to the first answer on the question you linked.

Attributes not being inherited from parent by subclasses

I'm learning to code in python. Currently, I'm at Obstacles and Classes however I have this issue where the attributes doesn't transfer from parent, and sometimes it oddly works. What seems to be the problem?
>>> class Things:
pass
>>> class Inanimate(Things):
pass
>>> class Animate(Things):
pass
>>> class Animals(Animate):
pass
>>> class Mammals(Animals):
pass
>>> class Giraffes(Mammals):
pass
>>> class Animals(Animate):
def breathe(self):
print("breathes")
>>> class Animals(Animate):
def move(self):
print("moves")
>>> class Animals(Animate):
def eat_food(self):
print("eats food")
>>> class Animals(Animate):
def jump(self):
print("jumps in the air")
>>> class Mammals(Animals):
def feeds_young_with_milk(self):
print("feeds young with milk")
>>> class Giraffes(Mammals):
def eat_leaves_from_trees(self):
print("eat leaves from trees")
>>> reginald = Giraffes()
>>> reginald.move()
Traceback (most recent call last):
File "<pyshell#44>", line 1, in <module>
reginald.move()
AttributeError: 'Giraffes' object has no attribute 'move'
>>> reginal.breathes()
Traceback (most recent call last):
File "<pyshell#45>", line 1, in <module>
reginal.breathes()
NameError: name 'reginal' is not defined
>>> reginald.breathes()
Traceback (most recent call last):
File "<pyshell#46>", line 1, in <module>
reginald.breathes()
AttributeError: 'Giraffes' object has no attribute 'breathes'
>>> reginald.eat_food()
Traceback (most recent call last):
File "<pyshell#47>", line 1, in <module>
reginald.eat_food()
AttributeError: 'Giraffes' object has no attribute 'eat_food'
>>> reginald.jump()
jumps in the air
>>>
Unlike in, say, Ruby, redefining a class doesn't add more stuff to the existing definition. Every time you write class Animals(Animate): ..., you're defining an entirely new class with no connection to any previous class with that name, and replacing the class object that the name Animals used to refer to. The new class doesn't have the methods of the old.
Stop redefining the same classes 5 times. Completely define a class the first time around.

Accessing attribute from parent class inside child class

When I access an attribute from the parent class via the child class like this all works fine:
class A():
a=1
b=2
class B(A):
c=3
d=B.a+B.b+B.c
print d
But if I try to access an attribute from the parent class inside the child class like this, it doesn't work:
class A():
a=1
b=2
class B(A):
c=3
d=a+b+c
print d
I receive the error: name 'a' is not defined
Let assume that I have many equation like d=a+b+c (but more complicated) and I can't edit them - I have to call in class B "a" as "a", not "self.a" or "something.a". But I can, before equations, do A.a=a. But it is not the smartest way to reload all variables manually. I want to bypass it using inheritance. Is it possible or i should do all manually? Or maybe it is 3th route in this code?
During the class definition, none of the inherited attributes are available:
>>> class Super(object):
class_attribute = None
def instance_method(self):
pass
>>> class Sub(Super):
foo = class_attribute
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
class Sub(Super):
File "<pyshell#7>", line 2, in Sub
foo = class_attribute
NameError: name 'class_attribute' is not defined
>>> class Sub(Super):
foo = instance_method
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
class Sub(Super):
File "<pyshell#9>", line 2, in Sub
foo = instance_method
NameError: name 'instance_method' is not defined
You can't even access them using super, as the name of the subclass isn't defined within the definition block*:
>>> class Sub(Super):
foo = super(Sub).instance_method
Traceback (most recent call last):
File "<pyshell#11>", line 1, in <module>
class Sub(Super):
File "<pyshell#11>", line 2, in Sub
foo = super(Sub).instance_method
NameError: name 'Sub' is not defined
The only way to access the inherited attributes at definition time is to do so explicitly, using the name of the superclass:
>>> class Sub(Super):
foo = Super.class_attribute
>>> Sub.foo is Super.class_attribute
True
Alternatively you can access them within class or instance methods, but then you need to use the appropriate prefix of the class (conventionally cls) or instance (conventionally self) parameter.
* for anyone thinking "ah, but in 3.x you don't need arguments to super":
>>> class Sub(Super):
foo = super().instance_method
Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
class Sub(Super):
File "<pyshell#6>", line 2, in Sub
foo = super().instance_method
RuntimeError: super(): no arguments
That's only true inside instance/class methods!
I may be wrong on this, but are you sure you don't want rather this?
class A(object):
def __init__(self):
self.a = 1
self.b = 2
class B(A):
def __init__(self):
super(B, self).__init__()
self.c = 3
#property
def d(self):
return self.a + self.b + self.c
BB = B()
print BB.d
or, as jonrsharpe pointed out:
class A():
a=1
b=2
class B(A):
c=3
d=A.a+A.b+c
print B.d

Defining __getattr__ and __getitem__ on a function has no effect

Disclaimer This is just an exercise in meta-programming, it has no practical
purpose.
I've assigned __getitem__ and __getattr__ methods on a function object, but
there is no effect...
def foo():
print "foo!"
foo.__getitem__ = lambda name: name
foo.__getattr__ = lambda name: name
foo.baz = 'baz'
Sanity check that we can assign properties to a function:
>>> foo.baz
'baz'
Neat. How about the "magic getters"?
>>> foo.bar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'bar'
>>> foo['foo']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'function' object is not subscriptable
>>> getattr(foo, 'bar')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'bar'
Is it possible to have a "magic getter" on a function object?
Nope! Assigning __getitem__ to an instance doesn't work on any type of object:
>>> class A(object):
... pass
...
>>> a = A()
>>> a.__getattr__ = lambda name: name
>>> a.foo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'foo'
And you can't define __getattr__ on the built-in function type:
>>> import types
>>> types.FunctionType.__getitem__ = lambda name: name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'function'
And you can't subclass types.FunctionType:
>>> import types
>>> class F(types.FunctionType):
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
type 'function' is not an acceptable base type
At least on new-style classes (which are the only kind in Python 3 and the kind you should be using in Python 2), Python only looks for magic methods on the class (and its ancestors), never on the instance. Docs here.
And of course you can't modify the function type, or derive from it. As you've found, however, any class with a __call__() method makes callable instances, so that's the way to do it.
AHHA! Use __call__, and wrap the function in F()
class F(object):
def __init__(self, fn):
self.__dict__['fn'] = fn
def __call__(self, *args, **kwargs):
return self.fn(*args, **kwargs)
def __getitem__(self, name):
return name
def __getattr__(self, name):
return name
>>> foo = F(foo)
>>> f.bar
'bar'
>>> f['foo']
'foo'
>>> foo()
foo!

Remove class attribute in inherited class Python

Consider such code:
class A ():
name = 7
description = 8
color = 9
class B(A):
pass
Class B now has (inherits) all attributes of class A. For some reason I want B not to inherit attribute 'color'. Is there a possibility to do this?
Yes, I know, that I can first create class B with attributes 'name' and 'description' and then inherit class A from B adding attribute 'color'. But in my exact case, B is actually a reduced version of A, so for me it seems more logical to remove attribute in B (if possible).
I think the best solution would be to change your class hierarchy so you can get the classes you want without any fancy tricks.
However, if you have a really good reason not to do this you could hide the color attribute using a Descriptor. You'll need to be using new style classes for this to work.
class A(object):
name = 7
description = 8
color = 9
class Hider(object):
def __get__(self,instance,owner):
raise AttributeError, "Hidden attribute"
def __set__(self, obj, val):
raise AttributeError, "Hidden attribute"
class B(A):
color = Hider()
You'll then get an AttributeError when you try to use the color attribute:
>>> B.color
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __get__
AttributeError: Hidden attribute
>>> instance = B()
>>> instance.color
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __get__
AttributeError: Hidden attribute
>>> instance.color = 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __set__
AttributeError: Hidden attribute
You can supply a different value for color in B, but if you want B not to have some property of A then there's only one clean way to do it: create a new base class.
class Base():
name = 7
description = 8
class A(Base):
color = 9
class B(Base):
pass

Categories

Resources