Can you reference a class name with a variable? - python

I am making an RPG text based game with classes and subclasses. I'm importing other python files, each of which have their own classes. Each python file is the name of their RPG main class, each of which have multiple classes for the respective RPG subclass. My question is, if I wanted to access a variable from one of these classes from the main file (E.G. if I wanted to access the bard's spellist I'd do Bard.Bard.Spellist, (First bard being the file import, second being the name of the class), could I use a variable like:
x = input("Enter class from which to access the spellist: )
print(x.x.spellist)
hope that makes sense!
This is what I tried:
x = Bard
for item in x.x.SpellBook:
print(item, ":", x.x.SpellBook[item])
I expected it to print a list of the names and spell level like this:
(spellname) : (spellevel)
It comes up with this error:
Traceback (most recent call last):
File "main.py", line 82, in <module>
for item in x.x.SpellBook:
AttributeError: module 'ClassFold.Bard' has no attribute 'x'

While you can do what you're asking (through a combination of globals() and getattr), it's a bad idea to do so. Your variable names should not be data in your program. Variable names are for you the programmer to use, not for your users.
Instead, use a dictionary or some other data structure to map from one kind of data (e.g. a string like 'bard') to another kind of data (e.g. the bard.Bard class). Use that mapping to process the user input into the data you actually need to use.
Here's a very simplified example:
class Bard:
spell_list = ['x', 'y', 'z']
class_name_mapping = {'bard': Bard, 'fighter': ...}
user_class_name = input('What class is your character? ')
user_class = class_name_mapping[user_class_name.lower()]
user_spell_list = user_class.spell_list
print(f'Your spells are: {", ".join(user_spell_list)}.')

Related

PYTHON: I want to class all of my txt file data but everytime i try to it says that i am missing the data required

This is the code I have designed as of now.
import sys #so that later on I can exit easily
CoinCountData = open("CoinCountData.txt","r")
class Volunteer:
def __init__(self,name,coin_type,weight_of_bag,TrueCount):
self.name = (name)
self.coin_type = (coin_type) #a function allowing me to class the data
self.weight_of_bag = (weight_of_bag)
self.TrueCount = (TrueCount)
I also have a little test part of data which is an attempt to make the second row of my txt file into a class
volunteer1 = Volunteer(CoinCountData.readlines()[1])
however, I am prompted with the following message.
Traceback (most recent call last):
File "C:\Users\Owner\Desktop\CW\!Coincount\coincount.py", line 14, in <module>
volunteer1 = Volunteer(CoinCountData.readlines()[1])
TypeError: __init__() missing 3 required positional arguments: 'coin_type', 'weight_of_bag', and 'TrueCount'
I do not understand how to actually class this data. It seems that it thinks that the entire thing is a name, I figured out that that is what the problem is by changing the code for an experiment:
import sys #so that later on I can exit easily
CoinCountData = open("CoinCountData.txt","r")
class Volunteer:
def __init__(self,name):
self.name = (name)
volunteer1 = Volunteer(CoinCountData.readlines()[1])
print(volunteer1.name)
When I ran the program it printed the entire line (Malcolm,1p,3356.00,Y)
Just for context, this is for my project for making a coin counter, the data given to me for the coin counting is as follows:
Abena,5p,325.00,Y
Malcolm,1p,3356.00,Y
Jane,£2,120.00,Y
Andy,£1,166.25,N
Sandip,50p,160.00,Y
Liz,20p,250.00,Y
Andy,20p,250.00,Y
Andy,50p,160.00,Y
Jane,£1,183.75,N
Liz,£,179.0,N
Liz,50p,170.0,N
Jane,50p,160.0,Y
Sandip,£1,183.0,N
Jane,£2,132.0,N
Abena,1p,3356.0,N
Andy,2p,250.0,N
Abena,£1,175.0,Y
Malcolm,50p,160.0,Y
Malcolm,£2,175.0,N
Malcolm,£1,175.0,Y
Malcolm,1p,356.0,Y
Liz,20p,250.0,Y
Jane,£2,120.0,Y
Jane,50p,160.0,Y
Andy,£1,175.0,Y
Abena,1p,359.56,N
Andy,5p,328.5,N
Andy,£2,108.0,N
Malcolm,£2,12.0,N
The data is listed vertically so it shows Abena,5p,325.00,Y and then below is Malcolm,1p,3356.00,Y etc
I understand the issue that the commas within the text file do not work as actual legitimate commas within python, I am not allowed to change the actual text file either so please let me know how I can make a full class which inputs all variables.
You should be iterating over the open file and splitting each line to provide the required parameters:
class Volunteer:
def __init__(self,name,coin_type,weight_of_bag,TrueCount):
self.name = name
self.coin_type = coin_type #a function allowing me to class the data
self.weight_of_bag = weight_of_bag
self.TrueCount = TrueCount
with open("CoinCountData.txt","r") as CoinCountData:
volunteers = []
for line in CoinCountData
volunteers.append(Volunteer(*line.strip().split(',')))
The error message you get is pretty straightforward.
Traceback (most recent call last): File "C:\Users\Owner\Desktop\CW!Coincount\coincount.py", line 14, in volunteer1 = Volunteer(CoinCountData.readlines()[1]) TypeError: init() missing 3 required positional arguments: 'coin_type', 'weight_of_bag', and 'TrueCount
It basically tells you that 3 parameters are missing when you're instanciating your Volunteer object : coin_type, weight_of_bag and TrueCount (which by the way should be named true_count to be consistent with the rest of your parameters).
Now, if you take a closer look at your code :
class Volunteer:
def __init__(self,name,coin_type,weight_of_bag,TrueCount):
...
You see here that when instanciating your Volunteer object, you need to give it 4 parameters : name, coin_type, weight_of_bag and TrueCount (self does not count as a parameter you need to provide).
Now, when you're instanciating your object, you do as follow :
volunteer1 = Volunteer(CoinCountData.readlines()[1])
You only give one parameter (that is, name). You also need to provide values for coin_type, weight_of_bag and TrueCount parameters.
It should look like something like that:
volunteer1 = Volunteer(CoinCountData.readlines()[1], "some coin type", 42, 14)
where name has the value of CoinCountData.readlines()[1], coin_type has the value some coin type and so on.
Readlines
I think you'll go into some more issues afterward, as your usage of readlines() seems incorrect in your case.
The readlines() method returns a list containing each line in your file (https://docs.python.org/3/distutils/apiref.html?highlight=readlines#distutils.text_file.TextFile.readlines).
So you will need a loop somewhere to loop over each line if you want to do it the way you've started.
CSV
The data file you're working with is a CSV (comma separated values). It means each line is a data entry, with each value separated by a comma.
You should have a look on the Python doc on how to read an CSV file : https://docs.python.org/3.6/library/csv.html
readlines returns a list of strings, each of the strings representing a line from the file.
You need to split the lines into their components, which you could do using
CoinCountData.readlines()[1].split(',')
You could then use argument unpacking to create an instance of the Volunteer class:
Volunterr(*CoinCountData.readlines()[1].split(','))
Note the asterisk (*).

I'm getting Name error for my python class attributes

I've just started learning Python and I was trying out class concept and came across this error and cant find out what I've done wrong ! Can someone point out ?!
class animal:
def __init__(self,name,ani,age,location):
self.name= name
self.ani = ani
self.age = age
self.location = location
def info(self):
print("I'm a {0} called {1} and I'm {2} years old, living in {3}".format(ani,name,age,location))
Arun = animal(Martin,Dog,7,Amazon)
Arun.info()
And the error is :
Traceback (most recent call last): File
"C:\Users\DELL\Desktop\python\class_trail.py", line 12, in <module>
Arun = animal(Martin,Dog,7,Amazon) NameError: name 'Martin' is not defined
So a couple of things have gone wrong with your code:
You are passing in variable names, not strings.
When you call Arun = animal(Martin,Dog,7,Amazon), Python looks for a variable name called Martin, but doesn't find one and raises NameError.
What you probably intended was Arun = animal('Martin','Dog',7,'Amazon')
Your .info() method needs to refer to self.ani and self.age etc., because those data items are bound up inside the object.
Arun = animal(Martin,Dog,7,Amazon)
^ you want to pass here string values, but instead you're passing there variables which are not defined. To pass there strings you have to wrap them into quotes like this:
Arun = animal("Martin","Dog",7,"Amazon")
The error you are getting refers to the fact that no variable Martin is defined in the code, before you use it. What you probably meant to do is
Arun = animal("Martin", "Dog", 7, "Amazon")
Note the quotes around the strings you are passing as parameters, as opposed to how python interprets your code, where this would work:
name = "Martin"
animal = "Dog"
age = 7
location = "Amazon"
Arun = animal(name, animal, age, location)
Extra: something you may want to get used to while learning to code in Python are its good practices. Python classes are typically declared with a capital letter (or actually PascalCase), like so class Animal():, while variable names are typically declared lower case (snake_case), like so arun = Animal(...).
There is a small exception: constants are declared all capital, like so EULER_CONSTANT = 0.577.

How to use a class variable in a function?

First of all I apologize if I have writen some word incorrectly - English is my second language.
But anyway I've been working on an text RPG for like a week and just started on an combat system and I have all of the player and enemy statistics in clases.
This is just part of my code but it's enough. So I have made a function which levels up my character.
class player:
def __init__(self):
self.name='Hero'
self.lvl=1
self.xp=0
self.lvl_next=25
self.str=1
self.dex=1
self.int=1
def pl_level(self):
Nstr=0
Ndex=0
Nint=0
while player.xp>=player.lvl_next:
player.lvl+=1
player.xp-=player.lvl_next
player.lvl_next=round(player.lvl_next*1.5)
Nstr+=1
Ndex+=1
Nint+=1
print('Level:', player.lvl)
print('STR {} +{} DEX {} +{} INT {} +{}'.format(player.str, Nstr, player.dex, Ndex, player.int, Nint))
player.str+=Nstr
player.dex+=Ndex
player.int+=Nint
print('Exp: '+str(player.xp))
print('To the next level: {}%'.format(int((player.xp/player.lvl_next)*100)))
print('Next:', player.lvl_next)
But I don't know why it just does not work.
I've tried to simplify my code because well maybe thats how i'll find the problem. But it just keeps shoving me this error.
Traceback (most recent call last):
File "F:\2XK_\Coding\Python\Python_Battle\Ulfberht\leveling_system.py", line 99, in <module>
pl_level()
File "F:\2XK_\Coding\Python\Python_Battle\Ulfberht\leveling_system.py", line 11, in pl_level
while player.xp>=player.lvl_next:
AttributeError: type object 'player' has no attribute 'xp'
Even tho you can see that in init there is self.xp.
So how can I fix this?
Use that like self.px inside other methods or else if you want to use like that only make it player().px instead of player.px.As your class needs to be to initialized first before using any of its variables or methods.
Better to access class variables in same class by using self as good practice.

Python 3 unique entries in a list tried using set on list but object not callable [duplicate]

This question already has answers here:
Why does code like `str = str(...)` cause a TypeError, but only the second time?
(20 answers)
Closed last month.
I tried to use this code from a tutorial at the REPL:
example = list('easyhoss')
The tutorial says that example should become equal to a list ['e', 'a', 's', 'y', 'h', 'o', 's', 's']. But I got an error instead:
>>> example = list('easyhoss')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'list' object is not callable
Why did this happen?
Seems like you've shadowed the builtin name list, which points at a class, by the same name pointing at an instance of it. Here is an example:
>>> example = list('easyhoss') # here `list` refers to the builtin class
>>> list = list('abc') # we create a variable `list` referencing an instance of `list`
>>> example = list('easyhoss') # here `list` refers to the instance
Traceback (most recent call last):
File "<string>", line 1, in <module>
TypeError: 'list' object is not callable
I believe this is fairly obvious. Python stores object names (functions and classes are objects, too) in namespaces (which are implemented as dictionaries), hence you can rewrite pretty much any name in any scope. It won't show up as an error of some sort. As you might know, Python emphasizes that "special cases aren't special enough to break the rules". And there are two major rules behind the problem you've faced:
Namespaces. Python supports nested namespaces. Theoretically you can endlessly nest them. As I've already mentioned, they are basically dictionaries of names and references to corresponding objects. Any module you create gets its own "global" namespace, though in fact it's just a local namespace with respect to that particular module.
Scoping. When you reference a name, the Python runtime looks it up in the local namespace (with respect to the reference) and, if such name does not exist, it repeats the attempt in a higher-level namespace. This process continues until there are no higher namespaces left. In that case you get a NameError. Builtin functions and classes reside in a special high-order namespace __builtins__. If you declare a variable named list in your module's global namespace, the interpreter will never search for that name in a higher-level namespace (that is __builtins__). Similarly, suppose you create a variable var inside a function in your module, and another variable var in the module. Then, if you reference var inside the function, you will never get the global var, because there is a var in the local namespace - the interpreter has no need to search it elsewhere.
Here is a simple illustration.
>>> example = list("abc") # Works fine
>>>
>>> # Creating name "list" in the global namespace of the module
>>> list = list("abc")
>>>
>>> example = list("abc")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'list' object is not callable
>>> # Python looks for "list" and finds it in the global namespace,
>>> # but it's not the proper "list".
>>>
>>> # Let's remove "list" from the global namespace
>>> del list
>>> # Since there is no "list" in the global namespace of the module,
>>> # Python goes to a higher-level namespace to find the name.
>>> example = list("abc") # It works.
So, as you see there is nothing special about Python builtins. And your case is a mere example of universal rules. You'd better use an IDE (e.g. a free version of PyCharm, or Atom with Python plugins) that highlights name shadowing to avoid such errors.
You might as well be wondering what is a "callable", in which case you can read this post. list, being a class, is callable. Calling a class triggers instance construction and initialisation. An instance might as well be callable, but list instances are not. If you are even more puzzled by the distinction between classes and instances, then you might want to read the documentation (quite conveniently, the same page covers namespaces and scoping).
If you want to know more about builtins, please read the answer by Christian Dean.
P.S. When you start an interactive Python session, you create a temporary module.
Before you can fully understand what the error means and how to solve, it is important to understand what a built-in name is in Python.
What is a built-in name?
In Python, a built-in name is a name that the Python interpreter already has assigned a predefined value. The value can be either a function or class object. These names are always made available by default, no matter the scope. Some of the values assigned to these names represent fundamental types of the Python language, while others are simple useful.
As of the latest version of Python - 3.6.2 - there are currently 61 built-in names. A full list of the names and how they should be used, can be found in the documentation section Built-in Functions.
An important point to note however, is that Python will not stop you from re-assigning builtin names. Built-in names are not reserved, and Python allows them to be used as variable names as well.
Here is an example using the dict built-in:
>>> dict = {}
>>> dict
{}
>>>
As you can see, Python allowed us to assign the dict name, to reference a dictionary object.
What does "TypeError: 'list' object is not callable" mean?
To put it simply, the reason the error is occurring is because you re-assigned the builtin name list in the script:
list = [1, 2, 3, 4, 5]
When you did this, you overwrote the predefined value of the built-in name. This means you can no longer use the predefined value of list, which is a class object representing Python list.
Thus, when you tried to use the list class to create a new list from a range object:
myrange = list(range(1, 10))
Python raised an error. The reason the error says "'list' object is not callable", is because as said above, the name list was referring to a list object. So the above would be the equivalent of doing:
[1, 2, 3, 4, 5](range(1, 10))
Which of course makes no sense. You cannot call a list object.
How can I fix the error?
Suppose you have code such as the following:
list = [1, 2, 3, 4, 5]
myrange = list(range(1, 10))
for number in list:
if number in myrange:
print(number, 'is between 1 and 10')
Running the above code produces the following error:
Traceback (most recent call last):
File "python", line 2, in <module>
TypeError: 'list' object is not callable
If you are getting a similar error such as the one above saying an "object is not callable", chances are you used a builtin name as a variable in your code. In this case and other cases the fix is as simple as renaming the offending variable. For example, to fix the above code, we could rename our list variable to ints:
ints = [1, 2, 3, 4, 5] # Rename "list" to "ints"
myrange = list(range(1, 10))
for number in ints: # Renamed "list" to "ints"
if number in myrange:
print(number, 'is between 1 and 10')
PEP8 - the official Python style guide - includes many recommendations on naming variables.
This is a very common error new and old Python users make. This is why it's important to always avoid using built-in names as variables such as str, dict, list, range, etc.
Many linters and IDEs will warn you when you attempt to use a built-in name as a variable. If your frequently make this mistake, it may be worth your time to invest in one of these programs.
I didn't rename a built-in name, but I'm still getting "TypeError: 'list' object is not callable". What gives?
Another common cause for the above error is attempting to index a list using parenthesis (()) rather than square brackets ([]). For example:
>>> lst = [1, 2]
>>> lst(0)
Traceback (most recent call last):
File "<pyshell#32>", line 1, in <module>
lst(0)
TypeError: 'list' object is not callable
For an explanation of the full problem and what can be done to fix it, see TypeError: 'list' object is not callable while trying to access a list.
If you are in a interactive session and don't want to restart you can remove the shadowing with
del list
In the league of stupid Monday morning mistakes, using round brackets instead of square brackets when trying to access an item in the list will also give you the same error message:
l=[1,2,3]
print(l[2])#GOOD
print(l(2))#BAD
TypeError: 'list' object is not callable
You may have used built-in name 'list' for a variable in your code.
If you are using Jupyter notebook, sometimes even if you change the name of that variable from 'list' to something different and rerun that cell, you may still get the error. In this case you need to restart the Kernal.
In order to make sure that the name has change, click on the word 'list' when you are creating a list object and press Shift+Tab, and check if Docstring shows it as an empty list.
Why does TypeError: 'list' object is not callable appear?
Explanation:
It is because you defined list as a variable before (i am pretty sure), so it would be a list, not the function anymore, that's why everyone shouldn't name variables functions, the below is the same as what you're doing now:
>>> [1,2,3]()
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
[1,2,3]()
TypeError: 'list' object is not callable
>>>
So you need it to be the default function of list, how to detect if it is? just use:
>>> list
<class 'list'>
>>> list = [1,2,3]
>>> list
[1, 2, 3]
>>> list()
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
list()
TypeError: 'list' object is not callable
>>>
How do i detect whether a variable name is a function? well, just simple see if it has a different color, or use a code like:
>>> 'list' in dir(__builtins__)
True
>>> 'blah' in dir(__builtins__)
False
>>>
After this, you should know why does TypeError: 'list' object is not callable appear.
Okay, so now...
How to fix this TypeError: 'list' object is not callable error?
Code:
You have to either do __builtins__.list():
>>> list = [1,2,3]
>>> __builtins__.list()
[]
>>>
Or use []:
>>> list = [1,2,3]
>>> []
[]
>>>
Or remove list variable from memory:
>>> list = [1,2,3]
>>> del list
>>> list()
[]
>>>
Or just rename the variable:
>>> lst = [1,2,3]
>>> list()
[]
>>>
P.S. Last one is the most preferable i guess :-)
There are a whole bunch of solutions that work.
References:
'id' is a bad variable name in Python
How do I use a keyword as a variable name?
How to use reserved keyword as the name of variable in python?
find out what you have assigned to 'list' by displaying it
>>> print(list)
if it has content, you have to clean it with
>>> del list
now display 'list' again and expect this
<class 'list'>
Once you see this, you can proceed with your copy.
For me it was a flask server returning some videos array (which I expected to be in json format..)
adding json.dumps(videos) fixed this issue
I was getting this error for another reason:
I accidentally had a blank list created in my __init__ which had the same name as a method I was trying to call (I had just finished refactoring a bit and the variable was no longer needed, but I missed it when cleaning up). So when I was instantiating the class and trying to call the method, it thought I was referencing the list object, not the method:
class DumbMistake:
def __init__(self, k, v):
self.k = k
self.v = v
self.update = []
def update(self):
// do updates to k, v, etc
if __name__ == '__main__':
DumbMistake().update('one,two,three', '1,2,3')
So it was trying to assign the two strings to self.update[] instead of calling the update() method. Removed the variable and it all worked as intended. Hope this helps someone.
You have already assigned a value to list.
So, you cannot use the list() when it’s a variable.
Restart the shell or IDE, by pressing Ctrl+F6 on your computer.
Hope this works too.
I found myself getting this error because I had a method that returned a list that I gave a #property decorator. I forgot about the decorator and was calling method() instead of just method which gave this same error.
Why error occurred?
Because you have named any of your list as "list" in current kernel.
example:
import operator
list = [1, 4, 5, 7, 9, 11]
#you are naming list as "list"
print("The sum is : ", end="")
print(list(itertools.accumulate(list1)))
print("The product is : ", end="")
print(list(itertools.accumulate(list1, operator.mul)))
Solution:
Simply restart the kernel.
Close the current interpreter using exit() command and reopen typing python to start your work. And do not name a list as list literally. Then you will be fine.
to solve the error like this one: "list object is not callable in python" even you are changing the variable name then please restart the kernel in Python Jutyter Notebook if you are using it or simply restart the IDE.
I hope this will work. Thank you!!!

Using built-in type(,,) function to create a dynamic module

I'm trying to use the type(,,) function to dynamically build a module. The module creates classes representing templates, and I need a new class for every .tex file that lives in a particular folder. For instance, if I have a a4-page-template.tex file, I need to create a class called A4PageTemplate.
I can create the type easily enough using the type statement; what I have so far looks like this;
#
# dynamictypes.py
#
import os, re
_requiredEnd = "-template.tex"
_packageDir = "C:\\Users\\...";
def _getClassName(name):
return re.sub("-(.)", lambda m: m.group(1).upper() , name) + "Template"
for file in os.listdir(_packageDir):
if file.lower().endswith(_requiredEnd):
_fileWithoutExt = file[:-len(_requiredEnd)]
_className = _getClassName(_fileWithoutExt)
_newClass = type(_className, (object,), dict(template=file))
print _newClass
Note the penultimate line creates the type, and the print statement that follows it shows that a type has been created for each template;
<class 'dynamictypes.PandocA4BookTemplate'>
<class 'dynamictypes.PandocBookTemplate'>
<class 'dynamictypes.PandocCompactTemplate'>
However, when I use the module, I can't make use of the type; if I write this consumer script;
import dynamictypes
myTemplate = dynamictypes.PandocA4BookTemplate()
I get this error;
Traceback (most recent call last):
File "C:\Users\steve.cooper\Desktop\driver.py", line 3, in <module>
myTemplate = dynamictypes.PandocA4BookTemplate()
AttributeError: 'module' object has no attribute 'PandocA4BookTemplate'
Can you think of a way for me to add the type I've created to the module, so that it is a first-class part of the module?
You have created the new class and assigned it to the global variable _newClass, but you're not storing this variable! Notice that if you do dynamictypes._newClass you will get the final type created.
You need to make a variable to hold each new class, as you create it:
globals()[ _className ] = _newClass
This registers _className as a global variable in the module, so that you can access it from outside.
By the way, your regex fails on a4-page-template.tex -- you get a4PageTemplate instead of A4PageTemplate.

Categories