Name Error python, a text adventure game - python-3.x

So i have a problem, that i don't quite understand why it's happening. I get a (Name Error global variable "value" is not defined) when it should be on my weapons class.
from items import *
class weapons(Item):
def __init__(self, name, attack_damage, lifesteal = 0):
super(weapons,self).__init__(name, value, quantity=1)
self.attack_damage = attack_damage
self.lifesteal = lifesteal
Here is the class that weapons is getting it from that already has value defined.
class Item(object):
def __init__(self, name, value, quantity=1):
self.name = name
self.raw = name.replace(" ","").lower()
self.quantity = quantity
self.value = value
self.netValue = quantity * value
def recalc(self):
self.netValue = self.quantity * self.value
I already have a piece of code similar to this that is working, but for some reason this value error is happening. I'm just going to include it.
from character import*
class player(character):
def __init__(self,name,hp,maxhp,attack_damage,ability_power):
super(player,self).__init__(name, hp, maxhp)
self.attack_damage = attack_damage
self.ability_power = ability_power
and the class that player is getting its stuff from
class character(object):
def __init__(self,name,hp,maxhp):
self.name = name
self.hp = hp
self.maxhp = maxhp
def attack(self,other):
pass
as you can see i did it here and this piece of code works when i call a player.

You need to add the value argument to the __init__ constructor of the weapons class.

super needs a parameter value but you did not pass it into the init

Related

Python (+Django) : use #property in success_url KO . while using it with get_success_url is OK (class based view)

I found a workaround for my issue but I need to know why the first above case doesn't work.
I need to pass a parameter (reman_pk) to my view but when I try :
class RepairCreateView(LoginRequiredMixin, CreateView):
#property
def reman_pk(self):
return int(self.kwargs['reman_pk'])
[...]
success_url = reverse_lazy(
'reman:update-reman', kwargs={'pk': reman_pk})
[...]
... I got an error
django.urls.exceptions.NoReverseMatch: Reverse for 'update-reman' with keyword arguments '{'pk': <property object at 0x10c20bbd0>}' not found. 1 pattern(s) tried: ['reman/update/(?P[0-9]+)/$']
But when in the same class based view I use :
def get_success_url(self, **kwargs):
if kwargs != None:
return reverse_lazy('reman:update-reman', kwargs={'pk': self.reman_pk})
... it's OK : an int is well passed in my URL.
I tried to pass int(reman_pk) in the first method ... not better.
I've already use #property in the past and always got a value (int/str) and not property object.
EDIT (FULL views.py)
success_url = reverse_lazy...is commented. I must use def get_success_url( ... instead. Otherwise I get the above mentioned error.
class RepairCreateView(LoginRequiredMixin, CreateView):
#property
def reman_pk(self):
return int(self.kwargs['reman_pk'])
# success_url = reverse_lazy(
# 'reman:repairs-list', kwargs={'pk': reman_pk})
success_message = "Nouvelle réparation créée"
form_class = RepairCreateForm
template_name = 'reman/repair_create_form.html'
def get_context_data(self, *args, **kwargs):
context = super(RepairCreateView, self).get_context_data(
*args, **kwargs)
context['title'] = 'Nouveau Repair'
context['pk'] = self.reman_pk
return context
def get_initial(self):
reman = Reman.objects.get(pk=self.reman_pk)
return {'reman': reman}
def get_success_url(self, **kwargs):
return reverse_lazy('reman:repairs-list', kwargs={'pk': self.reman_pk})
This is not related to django, this is related to python in general. When you want to access a class property within the class you always have to call self before!
class Tree:
fruits = 5
#property
def leafes(self):
return self.fruits * 5
def show_tree(self):
print(self.fruits)
print(self.leafes)
print(leafes) # THIS LINE WOULD ERROR
Edit after comment of OP
I don't know how to phrase this properly. Anyhow this keeps being a problem related to python and not to django. The reason is how classes work.
You probably know the def __init__(self): function. That is called when the class gets instanciated. After that function got called your class can use all the self attributes (class attributes). But class attributes like my fruits = 5 get assigned even before that def __init__(self) method is called. So all your assignments directly inside the body of the class do not have self yet.
class Tree:
fruits = 5
def __init__(self):
self.twigs = 10
self.weight = self.twigs + self.fruits # THIS WORKS
class Tree:
fruits = 5
weight = self.twigs + fruits # THIS DOES NOT WORK
def __init__(self):
self.twigs = 10
Last example does not work because at the moment you want to assign weight = self.twigs + fruits your class's __init__ function was not called yet. So you can not use self at that place.

OOP - Python - printing instance variable too when I call static method alone

Here in this code I am just calling out my static method, but it prints my instance variable too. Could you please explain the reason for that, and how to avoid them being printed?
Like below:
I am a static Method
None
class Player:
def __init__(self, name = None):
self.name = name # creating instance variables
#staticmethod
def demo():
print("I am a static Method")
p1 = Player()
print(p1.demo())
As Python docs says:
Print objects to the text stream file, separated by sep and followed
by end. sep, end, file, and flush, if present, must be given as
keyword arguments.
So you can return your message in method and then just print it:
class Player:
def __init__(self, name = None):
self.name = name # creating instance variables
#staticmethod
def demo():
return "I am a static Method"
p1 = Player()
print(p1.demo())

How to access outer class attribute from inner class

I'm having trouble understanding nested classes and have a specific problem
I have a model "Pair" which has an attribute "name." I'd like to have a nested class called "History" that passes variables "start" and "end" as well as its parent (Pair) variable's "name". A #property decorated function would then return the standard deviation of that list.
To call the standard deviation I'd be looking for something like:
pair = Pair(base_currency='USD', quote_currency='GBP')
print(pair.History(granularity='1D', start=start_date, end=end_date).std_dev
>>> 634
I think it should look something like the below, but cant figure out how to inherit the outer class attribute "name"
class Pair:
def __init__(self, base_currency, quote_currency):
self.base_currency = base_currency
self.quote_currency = quote_currency
self.name = '{base}-{quote}'.format(base=base_currency, quote=quote_currency)
self.history = self.History()
class History:
def __init__(self, granularity, start, end):
self.name = HOW DO I GET THE PARENT NAME HERE
self.granularity = granularity
self.start = start
self.end = end
def data(self):
return retrieve_history(product=self.name, granularity=self.granularity, start=self.start, end=self.end)
#property
def std_dev(self):
return stdev(self.data)

Multiple inheritance, super() and their correct use with arguments in Python

I'm trying to understand multiple inheritance in python. I think that "kinda" got it, but I'm missing a few pieces. I know that if I have two clases I can do something like:
class A():
def __init__(self,name):
self.name = name
class B(A):
def __init__(self,name):
A.__init__(self,name)
self.mm = False
self.name = name
b = B("Peter")
My problem is when I have more classes and each class has their own init arguments. At first glance, it makes like no sense to have something like this:
class A():
def __init__(self,name,arg_a1,arg_a2):
self.name = name
class B(A):
def __init__(self,name,arg_b1,arg_b2,arg_a1,arg_a2...):
A.__init__(self,name,arg_a1,arg_a2...)
self.mm = False
self.name = name
class C(B):
def __init__(self,name,arg_c1,arg_c2,arg_b1,arg_b2,arg_a1,arg_a2.........):
B.__init__(self,name,arg_b1,arg_b2,arg_a1,arg_a2...)
self.name = name
So I started to look how to do it in an efficient way and not just hardcode it. Thats when I came across with multiple inheritance and thats when my doubts started to arraise.
If I have 3 classes:
class A():
def __init__(self,name):
self.name = name
class B(A):
def __init__(self,name,*args,**kwargs):
super().__init__(*args,**kwargs)
self.mm = False
self.name = name
class C(B):
def __init__(self,a,j,*args,**kwargs):
super().__init__(*args,**kwargs)
self.a = a
self.j = j
c = C("p",1,5,name="p")
Why this give an error but adding name as an init argument does not?
In this other example, if I add another argument to A init's function the I get TypeError: __init__() got multiple values for argument 'name'.
class A():
def __init__(self,name,lastname):
self.name = name
self.lastname = lastname
class B(A):
def __init__(self,name,*args,**kwargs):
super().__init__(name,*args,**kwargs)
self.mm = False
self.name = name
class C(B):
def __init__(self,a,j,*args,**kwargs):
super().__init__(*args,**kwargs)
self.a = a
self.j = j
c = C("p",1,5,name="p")
So, after all this, several questions comes to my mind.
Why this TypeError is generated?
How can I make inheritance "smart"?
Do I always need to use *args and **kwargs with multiple inheritance?
And all this gets me to the point to the libraries I use daily. Probably some of them use this concetps (I don't know, I'm assuming). What happes when the user puts a kwarg that is not present in any class? How do python "knows" that name goes in class A and not class B or viceversa?

Decorators unexpectedly change constructor behavior in Python

Below, I show a simplified example of a more complicated code, but nonetheless, it fully represents the issue that I have encountered.
Part 1: this works fine, no issues:
class Animal():
def __init__(self, animal_name = "no name given"):
self.set_name(animal_name)
def get_name(self):
return self._animal_name
def set_name(self, animal_name):
self._animal_name = animal_name
class Dog(Animal):
def __init__(self, dog_breed = "no breed", dog_name = "no name given"):
self._dog_breed = dog_breed
super().__init__(dog_name)
def get_breed(self):
print(self._dog_breed)
x = Dog('Greyhound', 'Rich')
Part 2: after introducing getter & setter decorators, the code stops working:
class Animal():
def __init__(self, animal_name = "no name given"):
#THE LINE BELOW SEEMS TO CAUSE AN ISSUE
self.name(animal_name)
#property
def name(self):
return self._animal_name
#name.setter
def name(self, animal_name):
self._animal_name = animal_name
class Dog(Animal):
def __init__(self, dog_breed = "no breed", dog_name = "no name given"):
self._dog_breed = dog_breed
super().__init__(dog_name)
def get_breed(self):
print(self._dog_breed)
x = Dog('Greyhound', 'Rich')
Output: AttributeError: 'Dog' object has no attribute '_animal_name'
When I keep the decorators in Part 2 but change the constructor in the Animal class to:
class Animal():
def __init__(self, animal_name = "no name given"):
self._animal_name=animal_name
It works.
I am just curious why it doesn't work in the example above in Part 2?
Short answer:
The line
self.name(animal_name)
can be split in two parts:
tmp = self.name
tmp(animal_name)
First, self.name calls the getter and the result is treated as a function. The getter uses return self._animal_name and since the setter has never been called, the respective error occurs.
Long answer:
Let's take the following class:
class Animal:
def __init__(self, animal_name):
self.name(animal_name)
#property
def name(self):
return self._animal_name
#name.setter
def name(self, animal_name):
self._animal_name = animal_name
To understand what the line
self.name(animal_name)
actually does, you first need to understand decorators.
The code
#dec
def func(a, b, ...):
[...]
is equivalent to
def func_impl(a, b, ...):
[...]
func = dec(func_impl)
(except that you can not call func_impl directly). See, for example, PEP 318 for more information.
This means that you can write the Animal class from above without using decorators:
class Animal:
def __init__(self, animal_name):
self.name(animal_name)
def get_name(self):
return self._animal_name
name = property(get_name)
def set_name(self, animal_name):
self._animal_name = animal_name
name = name.setter(set_name)
In order to understand this code, you need to understand the builtin property, which is a class. See the python docs for detailed information.
The line name = property(get_name) creates an object of type property. When retrieving the value of the property, get_name is called.
The line name = name.setter(set_name) first calls name.setter(set_name), which creates a copy of the property, and then overwrites name with this copy. When assigning a value to the copy, set_name is called.
All in all, name is an object of type property that uses get_name as getter and set_name as setter.
How does this help?
You need to understand this: name is not a function. It is a property. It is not callable.
The problematic line
self.name(animal_name)
is actually equivalent to
self.get_name()(animal_name)
which this explains the error message: The constructor calls the getter, which tries to use return self._animal_name. But since the setter has not been called, yet, self._animal_name has not been set.

Resources