Having trouble with object creation - python

I am creating a program where a user can learn a number of skills which are predefined and are changed to a learned = True value later. I want to put all of the objects which are already learned into a list but can't find a way to collect all objects to sort through them and would appreciate if anyone could suggest one. This is my current strategy using a list in the class definition but it isn't working correctly so I would be appreciative if someone had a better way.
My class currently looks like this:
class attacks:
learned = False
def __init__(self,name,baseDMG,adMulti,apMulti,accuracy):
self.name = name
self.baseDMG = baseDMG
self.adMulti = adMulti
self.apMulti = apMulti
self.accuracy = accuracy
global adAbilities
global apAbilities
global mixedAbilities
adAbilities = []
apAbilities = []
mixedAbilities = []
if adMulti > 0 and apMulti > 0:
mixedAbilities.append(self)
elif adMulti > 0:
adAbilities.append(self)
elif apMulti > 0:
apAbilities.append(self)
But when I check the items in the lists it only stores one of the objects in the 2nd list and none in the others even though i have created these objects:
slash = attacks("slash",20,6,0,90)
smite = attacks("smite",20,0,6,90)
doubleStrike = attacks("doubleStrike",30,9,0,70)
blast = attacks("blast",30,0,9,70)
weaponThrow = attacks("weaponThrow",20,8,0,90)
Only the last object defined is the only one in any lists no matter which object is last defined (tested by printing lists after each object is defined).

Related

Function that returns a list of car models

I am writing a function that returns a list of car models. The input is a string of comma separated cars. Duplicate entries are not added to the result. The elements in the list should be in the same order as they appear in the input string. If the input string is empty, the result is an empty list. I modified it to support multiple model names, e.g. print(car_models("Tesla Model S,Skoda Super Lux Sport)) gives ['Model S', 'Super Lux Sport'].
def car_models(all_cars: str) -> list:
if not all_cars:
return []
all_models = []
cars = all_cars.split(",")
for car in cars:
unique_car = car.split(" ")
if unique_car[1] not in all_models:
all_models.append(" ".join(unique_car[1:]))
return all_models
While testing the code, an error occurred:
And I can't figure out what's wrong, can anyone tell me how to fix it?
Since you're appending " ".join(unique_car[1:]) to the list, you need to use the same thing when checking if already exists.
Solve this easily by assigning that to a variable, so you can use the same thing in the in test and append() call.
def car_models(all_cars: str) -> list:
if not all_cars:
return []
all_models = []
cars = all_cars.split(",")
for car in cars:
car_words = car.split(" ")
model = " ".join(car_words[1:])
if model not in all_models:
all_models.append(model)
return all_models
Checking for the existence of some object in a list will not perform optimally when the list is very large. Better to use a set.
Here are two versions. One without a set and the other with:
def car_models(all_cars):
result = []
for car in all_cars.split(','):
if (full_model := ' '.join(car.split()[1:])) not in result:
result.append(full_model)
return result
def car_models(all_cars):
result = []
resultset = set()
for car in all_cars.split(','):
if (full_model := ' '.join(car.split()[1:])) not in resultset:
result.append(full_model)
resultset.add(full_model)
return result
Note:
If order didn't matter (it does in this case) one could just use a set
First, I suppose you misspelled the command and should be like:
print(car_models("Tesla Model S","Skoda Super Lux Sport"))
And then, instead of:
unique_car = car.split(" ")
I should use something like:
unique_car = car[car.find(" ")+1:]
to have full name of the model compared. Even with:unique_brand = car[:car.find(" ")] you can be able to create a beautiful dictionary like: {'brand1':[model1, model2], 'brand2':...}
Besides, if the problem is coming from the test, please share your test code.

How can I properly use variables returned in other function / method?

Please bare with me since I am just starting to learn coding, and Python is my first language to go.
I struggle and can't really get to understand how the functions work.
I can't manage to call it and use later on when I need it in another function.
Can someone please help me understand the depth of it ?
My code doesn't work and I can't manage to understand how to grab the results from a function, in order to use those results for the end purpose.
This is something I tried in the project I am working on:
manly_coded_bag = []
female_coded_bag = []
feminine_coded_words = [
"agree",
"affectionate",
"child",
"cheer",
]
masculine_coded_words = [
"active",
"adventurous",
"aggressive",
"ambitios",
]
explanations = {
"feminine-coded": (
"This job ad uses more words that are subtly coded as feminine than words that are subtly coded as masculine"
),
"masculine-coded": (
"This job ad uses more words that are subtly coded as masculine than words that are subtly coded as feminine."
)
def men_coded_words(masc_bag, text):
add_text = text
man_coded_bag = masc_bag
for word in masculine_coded_words:
if word in add_text:
man_coded_bag.append(word)
return man_coded_bag
def women_coded_words(fem_bag, text):
add_text = text
woman_coded_bag = fem_bag
for word in feminine_coded_words:
if word in add_text:
woman_coded_bag.append(word)
return woman_coded_bag
def analise_and_explain_results(text, count_man, count_fem):
count_man_words = count_man
count_man_words = len(man_coded_bag)
count_woman_words = count_fem
count_woman_words = len(woman_coded_bag)
coding_score = count_woman_words - count_man_words
strengths_of_coding = ""
if coding_score == 0:
if count_man_words:
strengths_of_coding = "neutral"
else:
strengths_of_coding = "empty"
elif coding_score > 0:
strengths_of_coding = "feminine-coded"
else:
strengths_of_coding = "masculine-coded"
return count_man_words, count_woman_words, strengths_of_coding
def get_results(text):
user_input = text
user_input = input("add text here:").lower()
res = analise_and_explain_results(text, man_coded_bag,
woman_coded_bag)
# i am trying to use the returned variable strengths_of_coding and
is not accesible.
explain_results = explanations[strengths_of_coding]
return res, explain_results
get_results("random text added here, really whatever for testing purposes")
Right, so when I am calling get_results('text'), I get this error and I know where it is coming from, "name 'strengths_of_coding' is not defined", but I just don't know how to access that variable...
I'm stuck here and a little bit frustrated because I understand it's a noob mistake, yet still I can't get the hang of it after a week of stress and frustration.
Any feedback is welcome.
So it's hard to explain everything if you barely have any knowledge in OOP or coding in general. But in python, the return value of a function can be anything. None, a integer, a list, tuple, dictionary, object. Can even be a class definition. Only by looking at it, will you know exactly. That is called duck-typing; "If it walks like a duck and it quacks like a duck, then it must be a duck"
In this case, your analise_and_explain_results function does not return one thing, but several since it does this:
return count_man_words, count_woman_words, strengths_of_coding
So it actually returns a tuple with those three values inside. And these variables are scoped to that specific function, you cannot use them outside that function anymore. Note: For the sake of simplicity; let's just stick to not using them outside of the function since it's bad practice.
In your code, you then do this:
res = analise_and_explain_results(user_input, man_coded_bag, woman_coded_bag)
Which means that res at this point is actually the tuple holding the three values you are interested in. You have several ways to resolve this. But this easiest to follow is to just assign the values of variables like this:
count_man_words, count_woman_words, strengths_of_coding = analise_and_explain_results(user_input, man_coded_bag, woman_coded_bag)
This basically unpacks the tuple into three different values since it basically does this:
a, b, c = (1, 2 ,3)
Where before you did:
d = (1, 2, 3)
Unpacking is easy, as long as the item you unpack holds as many items as you're trying to assign;
a, b, c = d
If you have trouble grasping OOP and python I would suggest you learn to walk, before you run, which you're doing now IMO.
Follow some tutorials or videos explaining OOP and python. Or combine them like they do on realpython.
strengths_of_coding is only defined inside the analise_and_explain_results function. When you return the values of that function, they are no longer attached to the names you used inside the function
return count_man_words, count_woman_words, strengths_of_coding can be also written as return (count_man_words, count_woman_words, strengths_of_coding) - it means the return value of the function is a tuple with 3 elements that are values of each of the variables, and that tuple is assigned to res in res = analise_and_explain_results(user_input, man_coded_bag, woman_coded_bag)
Value of variable called strengths_of_coding inside the function is available as res[2] in get_results after you do the assignment to res
res = analise_and_explain_results(user_input, man_coded_bag, woman_coded_bag) turns res into a tuple with 3 elements. strengths_of_coding is the 3rd element in this tuple. So, you access it as res[2]. In python, when you return multiple stuff to one variable, the variable turns into a tuple. You can provide multiple variables to take each return. e,g, count_man_words, count_woman_words, strengths_of_coding = analise_and_explain_results(user_input, man_coded_bag, woman_coded_bag). Or, if you only need that one return then, strengths_of_coding = analise_and_explain_results(user_input, man_coded_bag, woman_coded_bag)[2].
Sorry for a late answer. This is how I ended up fixing my code with the kind help from people who answered, I have came to understand where I was making mistakes.
This is how I fixed my code, using the example above, just in case someone else struggle with grasping the basics.
As a bonus for any new beginner finding this useful, something I learned from someone else.
The last print statement is a very useful way to debug your code:
print("results are: %s" % results)
For example adding it at the end, you can see if you ended up getting the right results, like in my example, or you can add it in your code to see what results you return in each functions and so on.
user_input = "Active, child, whatever random text, testing"
text = user_input.lower()
# declare the coded words, feminine and masculine
feminine_coded_words = [
"agree",
"affectionate",
"child",
"cheer",
]
masculine_coded_words = [
"active",
"adventurous",
"aggressive",
"ambitios",
]
# declare explanations to use when we explain the results to the user.
explanations = {
"feminine-coded": (
"This job ad uses more words that are subtly coded as feminine than words that are subtly coded as masculine"
),
"masculine-coded": (
"This job ad uses more words that are subtly coded as masculine than words that are subtly coded as feminine."
),
"neutral": ("this is neutral"),
"empty": ("empty text"),
}
# initiate two empty variable where we add our coded words, when we find them.
def men_coded_words(text):
add_text = text
manly_coded_bag = []
for word in masculine_coded_words:
if word in add_text:
manly_coded_bag.append(word)
return manly_coded_bag
def women_coded_words(text):
add_text = text
feminine_coded_bag = []
for word in feminine_coded_words:
if word in add_text:
feminine_coded_bag.append(word)
return feminine_coded_bag
def feminine_counted_words(text):
woman_coded_bag = women_coded_words(text)
return len(woman_coded_bag)
def masculine_counted_words(text):
man_coded_bag = men_coded_words(text)
return len(man_coded_bag)
def coding_score(text):
count_fem_words = feminine_counted_words(text)
count_masc_words = masculine_counted_words(text)
return count_fem_words - count_masc_words
def explain_results(text):
strengths_of_coding = ""
count_masc_words = masculine_counted_words(text)
coding_score_results = coding_score(text)
if coding_score_results == 0:
if count_masc_words:
strengths_of_coding = "neutral"
else:
strengths_of_coding = "empty"
elif coding_score_results > 0:
strengths_of_coding = "feminine-coded"
else:
strengths_of_coding = "masculine-coded"
return strengths_of_coding
def get_results(text):
strenght_of_coding = explain_results(text)
return explanations[strenght_of_coding]
results = get_results(text)
print("results are: %s" % results)

Calling existing variable (list) reconstructed with StringVar()

I have a several lists that I am trying to activate or use that are named as follows:
public_forum_jr_soc = []
parliamentary_jr_soc = []
european_jr_soc = []
etc...
I have a bunch of radiobuttons that has variables to recreate these lists names, and it outputs something like this.
print(self.select_league.get()+self.select_age.get()+self.select_grade.get())
append_name = self.select_league.get()+self.select_age.get()+self.select_grade.get()
>>>
european_jr_soc
I try to proceed to use this list in as an argument for a function.
output_data(append_name)
def master_groper2(outof48):
for i in outof48:
if i[15] in ['ENGLISH','english','']:
i[15] = 'English'
elif i[15] in ['Mandarin','CHINESE',]:
i[15] = 'Chinese'
However, I get a IndexError: string index out of range as python reads it as european_jr_soc, instead of the actual list, despite sharing the same name, how could I have python have it read it as the variable list?

setting up iterative item query in python

I am trying to set up a function that will query an item for its sub components if those exists and return those else return the item itself.
Imagine an object that can contain more objects within it. To access those objects i would do object.GetSubComponentIds() now if that object contains sub objects it would return a list of those sub objects or EmptyList if there are none. In case that there are sub objects contained within it I want to keep going and then for each subobject i want to check if there are any subobjects contained within them. So for every SubObject.GetSubComponentIds() now if those do not contain anything then i would love to return them while maintaining nested structure of objects that they came from.
object1(contains 3 sub objects)
object2(contains 3 sub object and each sub object contains one more sub object)
object3(does not contain sub objects)
inputlist = [object1, object2]
outputlist = [[obj1sub1, obj1sub2, obj1sub3],[[obj2sub1sub1],[obj2sub2sub1],[obj2sub3sub1]],[obj3]]
I am interested in maintaining that nested list structure that will allow me to always trace back the origin of the sub object. Again, a method to get a sub object list is object.GetSubComponentIds() and it will either return a list or Empty List.
Can anyone help me set up an iterative function to retrieve them. Keep in mind that I do not know whether there are any sub objects contained within an object or haw many levels deep are they. It's basically that if it returns a list i need to check every item on that list for more sub objects.
Thank you in advance
Here's my humble first try:
#unwrap all elements to use with API
elements = []
for i in IN[0]:
elements.append(UnwrapElement(i))
#create element set from python list
elementSet = Autodesk.Revit.DB.ElementSet()
for i in elements:
elementSet.Insert(i)
#convert element set to List[Element]
setForCheck = List[Autodesk.Revit.DB.Element]()
elemIter = elementSet.ForwardIterator()
elemIter.Reset()
while elemIter.MoveNext():
curElem = elemIter.Current
setForCheck.Add(curElem)
#iterate throuh all elements to extract nested elements
setLoop = List[Autodesk.Revit.DB.Element]()
elemSet = List[Autodesk.Revit.DB.Element]()
itemOut = []
counter = 0
while setForCheck.Count >= 1:
setLoop.Clear()
for i in setForCheck:
itemOut.append(i)
if i.GetSubComponentIds().Count >= 1:
elem = Autodesk.Revit.DB.ElementSet()
for j in i.GetSubComponentIds():
elem.Insert(doc.GetElement(j))
elemIterA = elem.ForwardIterator()
elemIterA.Reset()
while elemIterA.MoveNext():
curElemA = elemIterA.Current
setLoop.Add(curElemA)
setForCheck.Clear()
elemIterB = setLoop.GetEnumerator()
elemIterB.Reset()
while elemIterB.MoveNext():
curElemB = elemIterB.Current
setForCheck.Add(curElemB)
counter += 1
if counter > 1000:
break
#Assign your output to the OUT variable
OUT = itemOut
You're using some specific libraries, like Autodesk, that I'm not familiar with. Let me answer your question in terms of an abstract example.
Suppose we're dealing with Thing objects, where Thing is defined as:
class Thing(object):
def __init__(self, name):
self.name = name
self.inside = []
We can make Things and put other things inside of them. The example you give in your post can be written:
ob1 = Thing("ob1")
ob1.inside.extend([Thing("ob1sub1"), Thing("ob1sub2"), Thing("ob1sub3")])
ob2 = Thing("ob2")
for i in xrange(1,4):
name = "ob2sub{}".format(i)
thing = Thing(name)
thing.inside.append(Thing(name + "sub1"))
ob2.inside.append(thing)
ob3 = Thing("ob3")
things = [ob1, ob2, ob3]
This makes a sort of tree. Now we'd like to return a nested list of all of the leaf nodes in the tree:
def search_things(things):
names = []
for thing in things:
if not thing.inside:
names.append(thing)
else:
names.append(search_things(thing.inside))
return names
A test:
>>> search_things(things)
[['ob1sub1', 'ob1sub2', 'ob1sub3'],
[['ob2sub1sub1'], ['ob2sub2sub1'], ['ob2sub3sub1']],
'ob3']
I'll let you transform this to your specific problem, but this is the general idea. Note that the algorithm is recursive, not iterative. You said you wanted an iterative algorithm -- and the above can be written iteratively -- but this gives you the idea.

(Python) Creating a list with a unique automatically generated name [duplicate]

This question already has answers here:
How can you dynamically create variables? [duplicate]
(8 answers)
Closed 8 years ago.
I am trying to automate populating a town by randomly generating households. I generate the name of the town, generate the number of households, the last name of each household and number of occupants in each. That much is fine. I am now, however, trying to create each individual, to generate a first name, a sex, an age and an occupation, and I'd like to store this data in a list as well, one list containing the attributes of each person. The problem I'm running into is that I want to use a for loop, something like:
#houseArray[currentFam][1] is the number of members in the current house.
for currentFam in range(houseArray[currentFam][1]):
uniquelyNamedArray[0] = genSex()
uniquelyNamedArray[1] = genFirstName()
uniquelyNamedArray[2] = genAge()
So... look at the data of the first household, use a for loop to iterate through each member assigning stats, then go to the next household and do the same, progressing through each household. My problem lies in not knowing how to assign a unique name to each array created by the for loop. It doesn't really matter what the name is, it could be anything as long as each person has their own uniquely named array storing their attributes.
Use a dictionary with the person's name as the key. Like:
people = {}
people["Billy Bloggs"] = ['23','Male','263 Evergreen Tce'] # store to dict
print ( people["Billy Bloggs"] ) # get stuff out of dict
Better still, give the attributes names by storing those as a dict as well:
people["Billy Bloggs"] = { 'Age':23, 'Gender':'M', 'Address':'263 Evergreen Tce' }
print ( people["Billy Bloggs"]['Age'] ) # Get billy's age
You can loop through the elements of a dictionary using the following syntax:
>>> mydict = {'a':'Apple', 'b':'Banana', 'c':'Cumquat'}
>>> for key, value in mydict.iteritems():
... print ('Key is :' + key + ' Value is:' + value)
...
Key is :a Value is:Apple
Key is :c Value is:Cumquat
Key is :b Value is:Banana
Note that there is no guarantee on the order of the data. You may insert data in the order A, B, C and get A, C, B back.
Note: The keys of a dict, in this case the person's name, are constrained to be unique. So if you store data to the same name twice, then the first key:value pair will be overwritten.
mydict["a"] = 5
mydict["a"] = 10
print (mydict["a"]) # prints 10
Sidenote: some of your gen*() functions could almost certainly be replaced by random.choice():
import random
first_names = ['Alice','Bob','Charlie','Dick','Eliza']
random_first_name = random.choice(first_names)
Keep data out of your variable names and just store them in a dict.
First, while you haven't shown us the surrounding code, you are probably relying too much on global variables. Rather than trying to create uniquely named arrays for each family member simply do something like this:
Don't really do this (I'll tell you why in a minute)
#houseArray[currentFam][1] is the number of members in the current house.
for currentFam in range(houseArray[currentFam][1]):
family_member_info = []
family_member_info[0] = genSex()
family_member_info[1] = genFirstName()
family_member_info[2] = genAge()
# Pretend 2 is where we are storing the family member information list
houseArray[currentFam][2].append(family_member_info)
A better way
Don't use an array for this sort of thing - it gets very difficult very quickly to tell what is actually stored in which index. Even in your example you have to note that houseArray[currentFam][1] is storing the number of members in the current house.
I would use either a dictionary or a named tuple and store your information in there. That way you can do something like this:
from collections import namedtuple
# Create a class called "household"
# with three fields, "owner", "size" and "members"
household = namedtuple("household", "owner size members")
househould_array = []
# Create some households and put them in the array
household_array.append(household("Family #1", 3, []))
household_array.append(household("Family #2", 1, []))
household_array.append(household("Family #3", 7, []))
# Loop over every household in the household_array
for family in household_array:
# Each `household` namedtulpe's values can be accessed by
# attribute as well as by index number
# family[1] == family.size == 3
# (for Family #1)
for member_number in range(family.size):
# family[2] == family.members == []
# (before we put anything in it)
family.members.append(generate_family_member())
You are mixing program data with variable names. It is okay to call a variable something generic; you do this all the time: e.g. in your for-loop, you use currentFam rather than the name of the family. Asking to uniquely name the array makes (no offense) as much sense as either asking what to name currentFam (it doesn't matter what you name it), or alternatively trying to do:
Andersons[0] = genSex()
Andersons[1] = genFirstName()
Andersons[2] = genAge()
Longs[0] = genSex()
Longs[1] = genFirstName()
Longs[2] = genAge()
Smiths[0] = genSex()
Smiths[1] = genFirstName()
Smiths[2] = genAge()
...
Variables are separate from program data.
You should just name your array person, and store it with other arrays. Even better would be to define a class Person(object): ..., so you could do things like x.name and x.age, but you don't need to do that. For example:
class Person(object):
def __init__(self, **kw):
self.data = kw
self.__dict__.update(kw)
def __repr__(self):
return str('Person(**{})'.format(self.data))
__str__ = __repr__
M = Person.M = 'm'
F = Person.F = 'f'
ALL_PEOPLE = set()
for ...:
person = Person(name=..., age=..., sex=...)
people.add(person)
Then to find people:
def findPeople(name=None, age=None, custom=set()):
matchers = custom
if name!=None:
matchers.add(lambda x:name.lower() in x.name.lower())
if age!=None:
matchers.add(lambda x:age==x.age)
return set(p for p in ALL_PEOPLE if all(m(p) for m in matchers))
Demo:
ALL_PEOPLE = set([
Person(name='Alex', age=5, sex=M),
Person(name='Alexander', age=33, sex=M),
Person(name='Alexa', age=21, sex=F)
])
>>> pprint.pprint( findPeople(name='alex', custom={lambda p: p.age>10}) )
{Person(**{'age': 33, 'name': 'Alexander', 'sex': 'm'}),
Person(**{'age': 21, 'name': 'Alexa', 'sex': 'f'})}
Wow, I really enjoyed reading all of the other answers.
So many great suggestions including, but not limited to:
#Sean Vieira suggests named-tuples -- an excellent, light-weight choice;
#ninjagecko uses a neat trick to dynamically assign instance attributes;
#Li-aung Yip mentions using the built-in sqlite3 module.
Much if not all of what's here has already been suggested.
If nothing else I hope this answer is an introduction to what classes may provide beyond what is provided by other data-structures.
Caveat: If performance is a huge concern, modeling each entity as a class might be overkill.
from __future__ import division, print_function
class Town(object):
def __init__(self, name=None, country=None, area_km2=0, population=0):
self.name = name
self.area_km2 = area_km2
self.area_mi2 = self.area_km2 * 0.38610217499077215
self.population = population
self.households = []
#property
def total_households(self):
return len(self.households)
#property
def population_density_per_km2(self):
try:
return self.population / self.area_km2
except ZeroDivisionError:
return 0
#property
def population_density_per_mi2(self):
try:
return self.population / self.area_mi2
except ZeroDivisionError:
return 0
class Household(object):
def __init__(self, primary_lang='Esperanto'):
self.primary_lang = primary_lang
self.members = []
#property
def total_members(self):
return len(self.members)
class Person(object):
def __init__(self, age=0, gender=None, first_name=None):
self.age = age
self.gender = gender
self.first_name = first_name
if __name__ == '__main__':
londontown = Town(name='London',
country='UK',
area_km2=1572,
population=7753600)
print(londontown.population_density_per_km2)
print(londontown.population_density_per_mi2)
a_household = Household()
a_household.members.append(
Person(age=10, gender='m', first_name='john'),
)
a_household.members.append(
Person(age=10, gender='f', first_name='jane')
)
londontown.households.append(a_household)
print(londontown.total_households)
print(a_household.total_members)

Categories