class UserInput():
users=[]
def __init__(self, name,lista,listb,listc,listd):
self.name=""
self.lista=lista
self.listb=listb
self.listc=listc
self.listd=listd
#staticmethod
def create_new_user(x):
x=userinput("x","","","","")
users.append(x)
Im intending on making a function where new users are generated, only returning a name to the user and no lists yet, hence x in the name slot.
My Question: is this the correct usage of #staticmethod or did I miss the entire point of it?
To my understanding, it allows the user to use,in this case, userinput.create_new_user('tim') without having the class already pre-defined, tim=userinput("foo","","","","");it creates it on the spot.
What I was trying to turn the function create_new_users into:
#staticmethod
def create_new_user():
print("how many users do you want to create")
x=int(input())
y=0
while y < x:
print("assign the users names")
name = input("")
if name == "" or "None,none":
raise SyntaxError("name cannot be None or empty")
break
name=userinput("","","","","")
userinput.users.append(name)
y+=1
in a static method you could not use the class variable, your code should get
NameError: global name 'users' is not defined
edit:
use userinput.users.append
Using a #classmethod will be the easiest alternative for that.
class UserInput: # capitals! Look at PEP 8.
users = [] # rearranged to the top for better readability
def __init__(self, name, lista, listb, listc, listd):
self.name = ""
self.lista = lista
self.listb = listb
self.listc = listc
self.listd = listd
#classmethod
def create_new_user(cls): # no need for x if you overwrite it immediately
x = cls("x", "", "", "", "")
cls.users.append(x) # easier access to this static attribute
return x # for the caller having access to it as well.
It works as well if we subclass UserInput as it uses the new class then.
But note that x = cls("x", "", "", "", "") won't be very useful, though; better do
#classmethod
def create_new_user(cls, *a, **k): # no need for x if you overwrite it immediately
x = cls(*a, **k) # pass the arguments given by the caller to __init__.
cls.users.append(x) # easier access to this static attribute
return x # for the caller having access to it as well.
I can use that now this way:
a = UserInput("foo", "whatever", "is", "needed", "here")
or, if I choose to,
a = UserInput.create_new_user("foo", "whatever", "is", "needed", "here")
which additionally appends the new user to the list.
If you want to be able to shorten the arguments list, you can do so as well:
def __init__(self, name, lista=None, listb=None, listc=None, listd=None):
self.name = name
self.lista = lista if lista is not None else []
self.listb = listb if listb is not None else []
self.listc = listc if listc is not None else []
self.listd = listd if listd is not None else []
if they are really lists. If they are strings, another name would be appropriate and, as strings are immutable, you can simply do
def __init__(self, name, lista='', listb='', listc='', listd=''):
self.name = name
self.lista = lista
self.listb = listb
self.listc = listc
self.listd = listd
and call the stuff with
a = UserInput.create_new_user("foo", listc=...) # all others are left empty
b = UserInput("bar") # all are left empty
c = UserInput.create_new_user("ham", lista=..., listd=...) # all others are left empty
Now that you come up with a different task, I'll try to cope with that as well:
#classmethod
def create_new_users(cls): # several users!
print("how many users do you want to create")
num = int(input())
for _ in range(num): # simpler iteration
print("enter the user's name")
name = input("") # in 3.x, this is always a string, so it cannot be None...
# if name == "" or "None,none": # That won't work as you think.
if name == '' or name.lower() == 'none': # but why disallow the string 'None'?
# raise SyntaxError("name cannot be None or empty")
raise RuntimeError("name cannot be None or empty") # or ValueError or alike
# break not needed. raise jumps out without it as well.
user = cls(name, "", "", "", "") # name is an input, not an output.
cls.users.append(name)
But I wonder if the class is really the right place to store new users, and only those created with this function. Maybe it would be better to feed the users list directly in __init__ and let this function be at a higher level.
The advantage of using a #classmethod here is that you always work on the corret basis.
Imagine you have a UserInput with a __init__() method as above. Then you can subclass it and do
UserInput.create_new_users()Using a #classmethod will be the easiest alternative for that.
class UserInputStoring(UserInput):
users = [] # this is only here, not at the parent.
def __init__(self, *a, **k):
super(UserInputStoring, self).__init__(*a, **k) # pass everything up as it was used
self.users.append(self)
Now you can have your create_new_users() in the base class and be a #classmethod and it will pick the right __init__ to call depending on how you call it.
Related
Although I've seen similar questions about this on here none have really explained in a way I think applies to me. I'm working on an RPG game in python and I store my character's inventory in a text file. However when I try to return these inventory items as an Item() class object I'm having issues. Each item is stored as: 'level 10 armor of water' or something along these lines. They are stored as the item's name which contains all the information needed for the object. --> Item(item_type, item_level, item_element, name). Is there anyway to extract this data needed from the object's name in string form?
#inventory.txt:
['', '', '', 'level 10 armor of water', '', '', '', '', '', '']
#Item() constuctor
class Item(object):
def __init__(self, item_type, item_level, item_element, name):
self.item_type = item_type
self.item_level = item_level
self.item_element = item_element
self.name = name
#Inventory Constructor
class Inventory(object):
item_slot1 = ""
item_slot2 = ""
item_slot3 = ""
item_slot4 = ""
item_slot5 = ""
item_slot6 = ""
item_slot7 = ""
item_slot8 = ""
item_slot9 = ""
item_slot10 = ""
slots = [item_slot1, item_slot2, item_slot3, item_slot4, item_slot5, item_slot6, item_slot7, item_slot8, item_slot9, item_slot10]
I realize this isn't the most efficient way of doing things but all help is appreciated.
You can do this:
with open("inventory.txt", "r") as f:
arr = eval(f.read())
for item_string in arr:
item = Item.from_string(item_string)
Writing Item.from_string is going to be a bit cumbersome though, since the name doesn't appear to lend itself well to parsing (e.g. "level 10 armor of water" instead of "level 10|armor|of water" or something easier like that). I'd redesign your storage format, but if that isn't an option, you could use regular expressions, like so:
class Item:
#staticmethod
def from_string(item_string):
level_match = re.match("level (\d+)", item_string)
item_level = level_match.group(1)
type_match = re.match("(armor|sword|backpack|etc)", item_string)
item_type = type_match.group(1)
return Item(item_type, item_level)
Also, you will be executing whatever code is contained in inventory.txt. But since the game is in Python, somebody could just edit the source code for the game itself. Realistically it isn't a problem, imho, but keep it in mind.
Parsing text in this way, instead of using a structured format like json, will lead to problems.
But, in the meantime, you can load the attributes from the str as long as it has a structured/predictable format.
For instance, if we assume that the first two words are the string level followed by the level number then you can use that pattern so long as it's true 100% of the time.
class Item:
def __init__(self, item_type, item_level, item_element, name):
self.item_type = item_type
self.item_level = item_level
self.item_element = item_element
self.name = name
# A #classmethod is good for defining another type of constructor.
# In this example, the #classmethod is what builds the class out of
# the name str.
#classmethod
def load_from_name(cls, name_text):
name_text = name_text.strip() # remove all surrounding whitespace
if not name_text:
return None # the text is empty
words = name_text.split() # split the text into word tokens
if words[0] != "level":
raise ValueError("Must start with 'level'")
try:
level = int(words[1])
except ValueError:
raise ValueError("Second word must be valid int")
# Now we want all of the words before "of" to be the item_type
# and all of the words after "of" to be the element.
if "of" not in words:
raise ValueError("Missing 'of'")
item_type, element = " of ".split(" ".join(words[2:]))
# Finally we assemble the instance and return it
return cls(item_type, level, element, name_text)
Notice how many conditions we have to check for. There's definitely many checks and errors missing. Here's what a structured format looks like:
class Item:
def __init__(self, item_type, item_level, item_element, name):
self.item_type = item_type
self.item_level = item_level
self.item_element = item_element
self.name = name
#classmethod
def load_from_save_state(cls, state):
return cls(state["type"], state["level"], state["element"], state["name"])
Now, the data can be loaded from a json/yaml/whatever structured format super easily.
import json
item_config_json = """
{
"item_type": "water",
"level": 10,
"element": "armor",
"name": "level 10 armor of water"
}
"""
# In a real scenario, this would probably get a path name,
# and the json would contain a list of many dict objects.
def load_item_from_json(json_text):
state = json.loads(json_text)
return Item.load_from_save_state(state)
So, I'm working on a command line RPG for the sake of filling time, and re-stretching my Python muscles as I've been out of practice for a couple of years. I used to code in a really functional manner but I'm trying to get my head around object-orientated programming.
Preamble aside, I have an issue where after creating an instance of a class, my class variable is no longer being defined. I've made 2 versions of this which I'll use to demonstrate since I'm finding it hard to articulate.
Below I created a "character" class which I intended to use as a basis for both player characters and npcs. In the first draft I was updating this class, before realising it was going to affect subclasses, when I really just wanted it as a template. Either way, this particular code block worked; it adds the values of 2 dictionaries together, then assigns them to character.characterStats. It then prints them as per displayStats().
from collections import Counter
class character:
def __init__(self, *args, **kwargs):
pass
def __setattr__(self, name, value):
pass
characterRace = ''
characterStats = {}
charLocation = ''
charName = ''
class race:
def __init__(self):
pass
baseStatsDict = {
'Strength' : 5,
'Agility' : 5,
'Toughness' : 5,
'Intelligence' : 5 }
humanStatsDict = {
'Strength' : 1,
'Agility' : 1,
'Toughness' : 1,
'Intelligence' : 1 }
def displayRace():
print("Race: ", character.characterRace, "\n")
def displayStats():
for stat, value in character.characterStats.items():
print(stat, "=", value)
print("\n")
def raceSelection():
playerInput = input("I am a ")
playerInput
playerLower = playerInput.lower()
while "human" not in playerLower:
if "human" in playerLower:
character.characterStats = dict(Counter(race.baseStatsDict)+Counter(race.humanStatsDict))
character.characterRace = 'Human'
break
playerInput = input()
playerInput
playerLower = playerInput.lower()
playerChar = character()
raceSelection()
displayRace()
displayStats()
And this was the output:
Race: Human
Strength = 6
Agility = 6
Toughness = 6
Intelligence = 6
This however is the new code when I tried to tidy it up and turn the class into the template it was meant to be, and started using the class instance playerChar which for whatever reason can't assign the new value to playerChar.characterStats. playerChar.displayStats() prints the characterRace and characterStats variables as empty, even though they are assigned when the player enters the value human.
from collections import Counter
class character:
characterRace = ''
characterStats = {}
def __init__(self):
pass
def displayRace(self):
print("Race: ", self.characterRace, "\n")
def displayStats(self):
for stat, value in self.characterStats.items():
print(stat, "=", value)
print("\n")
class race:
def __init__(self):
pass
baseStatsDict = {
'Strength' : 5,
'Agility' : 5,
'Toughness' : 5,
'Intelligence' : 5 }
humanStatsDict = {
'Strength' : 1,
'Agility' : 1,
'Toughness' : 1,
'Intelligence' : 1 }
def raceSelection():
playerInput = input("I am a ")
playerInput
playerLower = playerInput.lower()
while "human" not in playerLower:
if "human" in playerLower:
playerChar.characterStats = dict(Counter(race.baseStatsDict)+Counter(race.humanStatsDict))
playerChar.characterRace = 'Human'
break
playerInput = input()
playerInput
playerLower = playerInput.lower()
playerChar = character()
raceSelection()
playerChar.displayRace()
playerChar.displayStats()
So this will output:
Race:
\n
\n
\n
So I know it's able to draw from the class race dictionaries and add their contents together as from the previous code. If I try and print the player.x characteristics it won't throw any errors so it recognises they exist. If anyone could explain to me what's going wrong and why in this new iteration, I'd be very grateful.
EDIT: So a friend and I have tried passing the class as an argument of raceSelection(), we've tried printing a string after each call/update of a variable and we've tried entering a string into the variable, printing it, then redefining the variable with a new string.
Input:
class character:
charRace = ''
charStats = {}
charLocation = ''
charName = ''
charString = "Cole said define a string."
Within the if statements:
if "human" in playerLower:
print("Oh, you're just a really ugly human.")
playerChar.charStats = dict(Counter(race.baseStatsDict)+Counter(race.humanStatsDict))
playerChar.charRace = 'Ugly Human'
print("playerChar.charString = ", playerChar.charString)
playerChar.charString = "Redefine."
print("playerChar.charString = ", playerChar.charString)
break
Output:
Oh, you're just a really ugly human.
playerChar.charString = Cole said define a string.
playerChar.charString = Cole said define a string.
Race:
It should not be character.characterStats.items(), but self.characterStats.items(). Similarly for all other values that belong to one, specific character.
Using the name of the class assigns a value that belongs to the class, and is the same for all objects you create. Lookup instance vs class attributes.
So, after trying to move the variables in and out of __init__, trying setattr(), trying to pass any sort of argument through the class just so it had some data, trying to run the instance of the class through a function, none of those solutions came to work in the end.
The solution turned out to be to create a subclass of character and manipulate that instead. I figured this would be alright as well since the player character will mutate throughout gameplay, and will never see further subclasses of itself.
This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
Closed 8 years ago.
I'd like to take a list from football player (which have few attributes like name, goals, club...) and add them to their club (which is another class) but it seems that I'm missing something because the list of players of the club is changing in the loop even if it's not called (I think I'm not correctly managing the instances of the players).
So, here is the code :
clubsWithGoal = []
class Player:
nickname = ""
imageURL = ""
numberOfGoal = 0
clubId = ""
def __init__(self, nickname, imageURL, clubId, numberOfGoal = 0):
self.nickname = nickname
self.imageURL = imageURL
self.clubId = clubId
self.numberOfGoal = numberOfGoal
def __str__(self):
return self.nickname
class Club:
Name = ""
ImageURL = u""
id = u""
numberOfGoal = 0
listOfPlayer = []
def __init__(self, id):
del self.listOfPlayer [:]
self.id = id
self.getData()
def __str__(self):
return self.Name
def getData(self):
try:
results = json.load(urllib.urlopen(
"http://worldcup.kimonolabs.com/api/clubs/" + self.id + "?apikey={youwon'tseeit}"))
self.ImageURL = results["logo"]
self.Name = results["name"]
except:
print(self.id)
def addGoal(self, numberOfGoalsToAdd):
self.numberOfGoal += numberOfGoalsToAdd
def addPlayer(self, player):
self.listOfPlayer.append(player)
print("added "+player.nickname+" to "+self.Name)
self.addGoal(player.numberOfGoal)
print("added the "+str(player.numberOfGoal)+" of "+player.nickname+" to "+self.Name)
So here are for the model class and here is the function which must sort the players and is not working:
def createAndOrderInClub(playerlist):
foundHisClub = False
for player in playerlist:
for club in clubsWithGoal:
# Case 1: The club already exists and the player is part of the club
if player.clubId == club.id:
club.addPlayer(player)
foundHisClub = True
break
# Case 2: The club doesn't already exist
if (foundHisClub == False):
newclub = Club(player.clubId)
newclub.addPlayer(player)
clubsWithGoal.append(newclub)
And an example that it's changing inside the loop (I'm java developer and new to Python):
I think the problem is that the listOfPlayer variable in the Club class is declared as a static class member, and is not initialized inside the __init__ function. This demo http://dbgr.cc/R demonstrates the point.
Besides the error above, it also looks like you're not resetting the foundHisClub variable inside the loop back to False. I would instead declare the foundHisClub variable inside the first for loop:
def createAndOrderInClub(playerlist):
for player in playerlist:
foundHisClub = False
for club in clubsWithGoal:
# Case 1: The club already exists and the player is part of the club
if player.clubId == club.id:
club.addPlayer(player)
foundHisClub = True
break
# Case 2: The club doesn't already exist
if (foundHisClub == False):
newclub = Club(player.clubId)
newclub.addPlayer(player)
clubsWithGoal.append(newclub)
listOfPlayer = []
This is a class attribute, meaning it's shared for all instances of your Club class. If you're coming from Java you can think of this as a static class variable. To make this list unique for each Club class, make sure you initialize it in the constructor with the self prefix.
def __init__(self, id):
del self.listOfPlayer [:]
self.id = id
self.listOfPlayer = []
self.getData()
Make sure you do the same for all the other variables you've defined at the class level:
Name = ""
ImageURL = u""
id = u""
numberOfGoal = 0
Remove these, and initialize them in the constructor using self.
The listOfPlayer container, as you declared it, is a "class variable" (a bit like a static class member in java), and since lists are mutable, whenever you modify this list in any instance of Club, it will change for all instances as well.
To fix it simply remove it from the class definition, and initialize the list in the __init__ function (the strings aren't problematic since they are immutable):
class Club:
Name = ""
ImageURL = u""
id = u""
numberOfGoal = 0
def __init__(self, id):
self.listOfPlayer = []
self.id = id
self.getData()
I have created a simple renaming script but I would like to ask for some advice so that I can refine the coding as well as honing my python scripting. Below is a small portion of code for now...
Though this may not be an issue in my point of view, but other than the two functions I have stated below, I have came to realize that almost all my functions, they contains objects = cmds.ls(selection=True) Though I do not mind retyping over and over again but I do believe there is a better way to rectify this problem.
However, when I tried to make them global before the class function, it is able to run until when I tired to execute one of the functions, it prompts an error saying that global name 'objects' is not defined or 'objects are not defined' etc.
Pertaining to that, any suggestions?
class mainWindow(QDialog):
def __init__(self, parent=None):
super(mainWindow, self).__init__(parent)
self.resize(300,225)
self.initUI()
self.createConnections()
def searchReplace(self):
wordSearch = str(self.searchTxt.text())
wordReplace = str(self.replaceTxt.text())
objCnt = cmds.ls(sl=True, sn=True)
if len(objCnt) == 0:
self.searchTxt.clear()
self.replaceTxt.clear()
cmds.warning('Nothing is selected')
else:
for wordString in sorted(objCnt):
if wordSearch in wordString:
newWordString = wordString.replace(wordSearch, wordReplace)
cmds.rename(wordString, newWordString)
self.searchTxt.clear()
self.replaceTxt.clear()
print '%s' %wordString + " has changed to : " + "%s" %newWordString
def addPrefix(self):
objects = cmds.ls(selection=True)
pfx = str(self.prefixTxt.text())
for item in objects:
if pfx == "":
cmds.warning('No prefix values in the field')
else:
cmds.rename(item, pfx + "_" + item)
self.prefixTxt.clear()
print 'Prefix added: %s_' %pfx
def addSuffix(self):
objects = cmds.ls(selection=True)
sfx = str(self.suffixTxt.text())
for item in objects:
cmds.rename(item, item + "_" + sfx)
self.suffixTxt.clear()
print 'Suffix added: _%s' %sfx
def numPadding(self):
objects = pm.ls(selection=True)
num = self.numTxt.text()
padding = self.paddingTxt.text()
if num != "" and padding !="":
try:
for currentWordStr in objects:
pad = ("%%0%ii" % int(padding)) % int(num)
newWordStr = currentWordStr.rename(currentWordStr.name() + "_" + pad)
except Exception:
self.numTxt.clear()
self.paddingTxt.clear()
cmds.warning('Input numerical values only')
else:
cmds.warning('Entries of Num or Padding are empty')
def selectHierarchy(self):
sel = cmds.ls(selection = True)
selCnt = len(sel)
if int(selCnt) == 0:
cmds.warning('Nothing is selected')
else:
objHierarchy = cmds.listRelatives(ad=True, type='transform', fullPath=True)
cmds.select(sel, objHierarchy)
def clearHierarchy(self):
sel = cmds.ls(selection = True)
selCnt = len(sel)
if int(selCnt) != 0 :
objHierarchy = cmds.select(clear=True)
else:
cmds.warning('Selection is empty. Nothing to be cleared')
All right, I think I understand what you tried, going to take a shot at an answer.
First, take a look at the following posts, should get you up to speed on globals:
Using global variables in a function other than the one that created them (great, succinct summary)
Variable scope outside of classes (example with classes)
So, first off, you don't need to use the global keyword when first declaring objects outside of the class definition. So, instead of:
global objects
objects = cmds.ls(selection=True)
class mainWindow(QDialog):
...
You would do:
objects = cmds.ls(selection=True)
class mainWindow(QDialog):
...
Then, your functions can just refer to "objects". If you need to WRITE to objects from within your functions in the class, then you need to first use the global keyword (this code assumes objects was defined before the class):
def my_method(self):
global objects
objects = some_function()
That said, I'm not 100% sure how the above code is being invoked, so it's possible that something else is causing "objects" to be undefined.
You might be better served with a class attribute here. You could do this:
class mainWindow(QDialog):
objects = cmds.ls(selection=True)
def my_func(self):
for item in self.objects:
do_stuff()
Keep in mind that objects would be the same for all instances of mainWindow, and any updates to objects in one instance will affect all other instances. That should be fine from what I can tell, but you should definitely become familiar with instance vs. class vs. module.
Hope that helps!
UPDATE: Whoops, changed the class attribute in one place, but not the other in the last example. Updated the example, it should make way more sense now.
This is my class:
class variable(object):
def __init__(self, name, name_alias, parents,values,table):
#name of the variable
self.name = ""
This is the function with problems:
f is a .txt file (opened in main function),
def read_problem(f):
list_of_variables=[]
entry=0;
for line in f:
words = line.split()
#enters only if it's not a comment
if (words[0]!='#'):
if (words[0]=="VAR"):
x=variable;
elif (words[0]=="name"):
x.name=words[1]
list_of_variables.append(x)
for i in range(len(list_of_variables)):
print(list_of_variables[i].name)
return
My .txt file is:
VAR
name MaryCalls
VAR
name JohnCalls
VAR
name Burglary
VAR
name Earthquake
VAR
name Alarm
What I get in that print(and thus, the list) is:
Alarm
Alarm
Alarm
Alarm
Alarm
But I wanted to have:
MaryCalls
JohnCalls
Burglary
Earthquake
Alarm
What's wrong? Why are all the previous entries of the list changing?
The line x=variable makes the x refer to the class variable. You never create any instances of that class, instead you repeatedly modify a class-level variable name.
At the end of the program, when you print, variable.name of course has the last value assigned to it, in this case 'Alarm'.
You'll see this if you do print(list_of_variables):
[<class '__main__.variable'>, <class '__main__.variable'>,
etc.
Change x = variable to x = variable(), and you'll see (for example):
[<__main__.variable object at 0x6ffffee65d0>, <__main__.variable object at 0x6ffffee6610>
etc.
1) If you want to initialize the name in the constructor of variable, you have to indent the assignment further.
def __init__(self, name, name_alias, parents,values,table):
#name of the variable
self.name = ""
2) Your main problem is, that you want to create a new instance of variable with this line:
x=variable;
You have to write:
x = variable();
The primary problem is when the code detects a line starting with "VAR" it assigns the class variable to x, not an instance of the class. To do that you need to call the class which ends up invoking the class' __init__() method if it has one. Yours does, but it expects 5 arguments, none of which are known at that time of creation.
The easiest thing to do in such a case is to assign each argument a default value that means "doesn't have a value yet", and assign those to the instance being created (self).
Here's what I mean:
class Variable(object):
def __init__(self, name="", name_alias="", parents=None, values=None,
table=None):
self.name = name
self.name_alias = name_alias
self.parents = parents
self.values = values
self.table = table
def read_problem(f):
list_of_Variables=[]
for line in f:
words = line.split()
# if not a comment
if words[0] != '#':
if words[0] == "VAR":
x = Variable() # construct empty Variable
elif words[0] == "name":
x.name = words[1]
list_of_Variables.append(x)
for var in list_of_Variables:
print(var.name)
return
def main():
filename = 'variables.txt'
with open(filename) as f:
read_problem(f)
main()