Python: class object is instantiated but not defined? - python

In the last line I get the error that my object ant003 is not defined. But why?
class Ant:
def __init__(self, name):
"""Initializes the data."""
self.name = name
self.food = 0
self.posx = 0
self.posy = 0
print("(Initializing {0})".format(self.name))
def searchFood(self):
self.food = self.food + 1
print("Ant {1} has {0} food.".format(self.food, self.name))
ant001 = Ant("Anton")
ant001.searchFood()
ant002 = Ant("Bob")
ants = {'ant003': None, 'ant004': None}
for name in ants.keys():
ants[name] = Ant(name)
print ants[name]
#print ant001
#print ant003
ant003.searchFood() # NameError: name 'ant003' is not defined

Setting an item of a dictionary does not affect namespace that contains the dictionary.
You can access dictionary value using dict[key]:
ants['ant003'].searchFood()
Did you mean to use globals? (not recommended)
...
for name in ants.keys():
globals()[name] = Ant(name) # <----
ant003.searchFood()

Related

Python: Create a single object for a set of arguments to the constructor [duplicate]

I defined a class named Experiment for the results of some lab experiments I am conducting. The idea was to create a sort of database: if I add an experiment, this will be pickled to a db before at exit and reloaded (and added to the class registry) at startup.
My class definition is:
class IterRegistry(type):
def __iter__(cls):
return iter(cls._registry)
class Experiment(metaclass=IterRegistry):
_registry = []
counter = 0
def __init__(self, name, pathprotocol, protocol_struct, pathresult, wallA, wallB, wallC):
hashdat = fn.hashfile(pathresult)
hashpro = fn.hashfile(pathprotocol)
chk = fn.checkhash(hashdat)
if chk:
raise RuntimeError("The same experiment has already been added")
self._registry.append(self)
self.name = name
[...]
While fn.checkhash is a function that checks the hashes of the files containing the results:
def checkhash(hashdat):
for exp in cl.Experiment:
if exp.hashdat == hashdat:
return exp
return False
So that if I add a previously added experiment, this won't be overwritten.
Is it possible to somehow return the existing instance if already existant instead of raising an error? (I know in __init__ block it is not possible)
You can use __new__ if you want to customize the creation instead of just initializing in newly created object:
class Experiment(metaclass=IterRegistry):
_registry = []
counter = 0
def __new__(cls, name, pathprotocol, protocol_struct, pathresult, wallA, wallB, wallC):
hashdat = fn.hashfile(pathresult)
hashpro = fn.hashfile(pathprotocol)
chk = fn.checkhash(hashdat)
if chk: # already added, just return previous instance
return chk
self = object.__new__(cls) # create a new uninitialized instance
self._registry.append(self) # register and initialize it
self.name = name
[...]
return self # return the new registered instance
Try to do it this way (very simplified example):
class A:
registry = {}
def __init__(self, x):
self.x = x
#classmethod
def create_item(cls, x):
try:
return cls.registry[x]
except KeyError:
new_item = cls(x)
cls.registry[x] = new_item
return new_item
A.create_item(1)
A.create_item(2)
A.create_item(2) # doesn't add new item, but returns already existing one
After four years of the question, I got here and Serge Ballesta's answer helped me. I created this example with an easier syntax.
If base is None, it will always return the first object created.
class MyClass:
instances = []
def __new__(cls, base=None):
if len(MyClass.instances) == 0:
self = object.__new__(cls)
MyClass.instances.append(self)
if base is None:
return MyClass.instances[0]
else:
self = object.__new__(cls)
MyClass.instances.append(self)
# self.__init__(base)
return self
def __init__(self, base=None):
print("Received base = %s " % str(base))
print("Number of instances = %d" % len(self.instances))
self.base = base
R1 = MyClass("apple")
R2 = MyClass()
R3 = MyClass("banana")
R4 = MyClass()
R5 = MyClass("apple")
print(id(R1), R1.base)
print(id(R2), R2.base)
print(id(R3), R3.base)
print(id(R4), R4.base)
print(id(R5), R5.base)
print("R2 == R4 ? %s" % (R2 == R4))
print("R1 == R5 ? %s" % (R1 == R5))
It gives us the result
Received base = apple
Number of instances = 2
Received base = None
Number of instances = 2
Received base = banana
Number of instances = 3
Received base = None
Number of instances = 3
Received base = apple
Number of instances = 4
2167043940208 apple
2167043940256 None
2167043939968 banana
2167043940256 None
2167043939872 apple
R2 == R4 ? True
R1 == R5 ? False
Is nice to know that __init__ will be always called before the return of the __new__, even if you don't call it (in commented part) or you return an object that already exists.

iterating objects in a list through a loop

So I'm just doing a learning project and I need some help.
class Car:
def __init__(self):
self.speed = 0
self.color = ""
self.weight = 0
self.engine = 4
self.name = ""
self.mpg = 25
self.maintenanceLog = []
self.oilChanges = []
#mutator methods
def setSpeed(self, sp):
self.speed = sp
def setColor(self, cl):
self.color = cl
def setWeight(self, w):
self.weight = w
def setEngine(self, e):
self.engine = e
def setName(self, n):
self.name = n
def setMpg(self, mpg):
self.mpg = mpg
def addOilChange(self, oc):
self.oilChanges.append(oc)
def addMaintenance(self, ml):
self.maintenanceLog.append(ml)
#accessor methods
def getSpeed(self):
return self.speed
def getColor(self):
return self.color
def getWeight(self):
return self.weight
def getEngine(self):
return self.engine
def getName(self):
return self.name
def getMPG(self):
return self.mpg
def getAllOilChanges(self):
print("")
print("----------OIL CHANGES----------")
for oc in self.oilChanges:
print(oc)
def getMaintenanceLogs(self):
print("")
print("----------MAINTENANCE LOGS----------")
for ml in self.maintenanceLog:
print(ml)
def setInfo(car):
car.setSpeed(int(input(f"Speed of {car}")))
car.setWeight(int(input(f"Weight of {car}")))
car.setName(input(f"Name of {car}"))
car.setColor(input(f"Color of {car}"))
car.setEngine(int(input(f"Engine of {car}")))
car.setMpg(int(input(f"Miles per Gallon of {car}")))
def getInfo(car):
print(f"Speed of {car} is {car.getSpeed()} mph.")
print(f"Weight of {car} is {car.getWeight()} pounds.")
print(f"Name of {car} is {car.getName()}.")
print(f"Color of {car} is {car.getColor()}.")
print(f"Engine cylinders of {car} are {car.getEngine()}.")
print(f"Miles per Gallon of {car} is {car.getMPG()}.")
def main():
carList = []
Object1= Car()
carList.append(Object1)
print(carList)
for obj in carList:
setInfo(obj)
getInfo(obj)
main()
Thats's the code, now, whenever I run it, I want to get asked Speed of Object1:
Instead, I get asked Speed of <main.Car object at 0x0000024B093CEFD0>:
How can I see the name of the object instead of that hash value... I want to keep adding objects with the class Car and then pass them through a loop to fill in the information regarding the object, but if the list of objects was [Object 1, Object 2, Object 3... Object N] I want it to refer to that name (Object 1) instead of <main.Car object at 0x0000024B093CEFD0>
You can pass the name to use in the prompts as an argument to setInfo().
def setInfo(car, name=None):
if name is None:
name = car.name
car.setSpeed(int(input(f"Speed of {name}")))
car.setWeight(int(input(f"Weight of {name}")))
car.setName(input(f"Name of {name}"))
car.setColor(input(f"Color of {name}"))
car.setEngine(int(input(f"Engine of {name}")))
car.setMpg(int(input(f"Miles per Gallon of {name}")))
and then use that in the loop.
for i, obj in enumerate(carList, 1):
setInfo(obj, f"Object {i}")
getInfo(obj)

How to change instance variable when another related instance variable is changed in a class?

I recently try to code a text based game. I want to change player proficiency when player level up. How should I change my code for this?
class Player:
def __init__(self,name,_class,_race):
self.name = name
self.level = 1
self.proficiency = (int(self.level/3)+1)*2
self.inventory = 0
self.skills = returnSkills()
self.stats = returnStats()
self._class = _class
self._race = _race
self.exp = 0
def levelUp(self):
self.level+=1
newPlayer = Player("Player","Barbarian","Human")
print(newPlayer.level)
for i in range(10):
print(newPlayer.level)
print(newPlayer.proficiency)
newPlayer.levelUp()
You can recalculate the proficiency attribute directly in the levelUp() function. Once you have updated the level attribute, that new value of level will be used to calculate the new proficiency.
def levelUp(self):
self.level+=1
self.proficiency = (int(self.level/3)+1)*2
You could make proficiency a property, so it is calculated from the current level each time it is referenced.
class Player:
def __init__(self,name,_class,_race):
self.name = name
self.level = 1
self.inventory = 0
self.skills = returnSkills()
self.stats = returnStats()
self._class = _class
self._race = _race
self.exp = 0
#property
def proficiency(self):
return (int(self.level/3)+1)*2
...
or you could leave it as a plain attribute, and recalculate it inside your levelUp method.

Adding to a class dynamically

I've created a new class and I'm trying to add to that class dynamically,
I've created a list, that I want to put multiple objects in, then I will iterate over that list in Django (is this the correct way of doing things?)
but I'm getting the below error
TypeError: __init__() takes exactly 9 arguments (1 given)
I know what the error means, I'm just wonder how I go about creating a new instance of my objects and adding to it on the fly easily?
### create User Object
class User:
def __init__(self, Policy, Level, StartDate, EndDate, StartTime, EndTime, Name, Mobile):
self.Policy = Policy
self.Level = Level
self.StartDate = StartDate
self.EndDate = EndDate
self.StartTime = StartTime
self.EndTime = EndTime
self.Name = Name
self.Mobile = Mobile
def __init__(self):
pass
### Get all the Polices ###
lstOnCall = []
for objPolicy in objPolicyData['escalation_policies']:
strPolicyName = objPolicy['name']
if strPolicyName.lower().find('test') == -1:
for objOnCall in objPolicy['on_call']:
objUser = User()
objUser.Policy = strPolicyName
objUser.Level = objOnCall['level']
objUser.StartDate = getDate(objOnCall['start'])
objUser.EndDate = getDate(objOnCall['end'])
objUser.StartTime = getTime(objOnCall['start'])
objUser.EndTime = getTime(objOnCall['end'])
objUser = objOnCall['user']
objUser.Name = objUser['name']
objUser.Mobile = getUserMobile(objUser['id'])
lstOnCall.append(objUser)
print lstOnCall
UPDATE:
adding the below works, i just need to know how to print the items now?
def __init__(self):
pass
the below
for item in lstOnCall:
print item()
returns
print item()
AttributeError: User instance has no __call__ method
You can write a dynamic constructor (def __init__) for your class so:
class User(object):
__attrs = ['Policy', 'Level', 'StartDate', 'EndDate', 'StartTime',
'EndTime', 'Name', 'Mobile']
def __init__(self, **kwargs):
for attr in self.__attrs:
setattr(self, attr, kwargs.get(attr, None))
def __repr__(self):
return ', '.join(
['%s: %r' % (attr, getattr(self, attr)) for attr in self.__attrs])
The variable __attrs stores the variables names. I used double underscore variable, so that it's inaccessible from extend.
user = User()
print(user.__attrs)
Traceback (most recent call last):
print(user.__attrs)
AttributeError: 'User' object has no attribute '__attrs'
Yes, there are other method to access double underscore variable, but no one will do that ;)
The function __repr__ return the string by calling print or str, if the function __str__ doesn't exist.
Now test it
>>> u1 = User(Name='user1')
>>> u2 = User(Name='user2', Policy=1, Level=3)
>>> print(u1)
Policy: None, Level: None, StartDate: None, EndDate: None, StartTime: None, EndTime: None, Name: 'user1', Mobile: None
>>> print(u2)
Policy: 1, Level: 3, StartDate: None, EndDate: None, StartTime: None, EndTime: None, Name: 'user2', Mobile: None
If you use my codes, you can print the items in your case so:
for item in lstOnCall:
print item
Other problem of your code
There aren't the definition Function overloading in Python. You can define multiple function with the same name in python. But it doesn't make any sense. Only the last definition remains in your class/module. The previous definitions will be overwritten. What you are doing with
class User:
def __init__(self, a, b, c):
...
def __init__(self):
pass
is False. It works in Java or C# but not in Python. The function def __init__(self, a, b, c) will be overwritten. Only the function def __init__(self) exists in your class.
You could set all of the parameters to __init__ to be None by default:
def __init__(self, Policy=None, Level=None, etc...):
Convert the positional parameters of your constructor method to named, optional parameters with a useful default value:
class User:
def __init__(self, Policy=Null, Level=1,
StartDate="2016-01-01", EndDate="2016-12-31",
StartTime="00:00", EndTime="23:59",
Name="UNKNOWN", Mobile=""):
self.Policy = Policy
self.Level = Level
self.StartDate = StartDate
self.EndDate = EndDate
self.StartTime = StartTime
self.EndTime = EndTime
self.Name = Name
self.Mobile = Mobile
Try this,
class User:
def __init__(self,*args,**kargs):
if len(kargs)==0 : ''' No param passed '''
self.Policy = 'Some'
self.Level = 0
else:
self.Policy = kargs['Policy']
self.Level = kargs['Level']
[..]
user= User()
user1= User(Policy='Some',Level=13)

Updating/deleting string variables in a class through the class' methods (functions) in python

I am a new user to python and am trying to update a class I have that is called Player(self, name, position) through a new class call API(object). In the class API(), I have the CRUD approach in creating a player, retrieving a player, updating a player, and lastly, deleting a player. I am struggling with the method in my update and delete function.
For update, I am calling self.retrieve_player[name] since it is a method that already reads the existing players from the initialized dictionary (found in the init file of class API.
Also, the main() function at the bottom is instantiating these methods by creating an instance of the object, calling API and the specified methods. e.g.:
c = API()
c.create_player('Buster Posey', 'Catcher')
c.retrieve_player('james')
c.update_player('Buster Posey', 'Firstbaseman')
The code I am struggling with is outputting an updated object that I created:
def update_player(self, name, position):
updated_player = self.retrieve_player(name)
updates = self.create_player(name, position)
updated_player = updates
return updates
print('updated now!')
def delete_player(self, name, position):
del_player = self.retrieve_player[name]
if name in del_player:
del self._team[key]
For update_player, I was playing with concept since the mentality for updating variables is something like:
a = "apple"
b = "orange"
x = a
x = b #now apple is replaced with the value orange
e.g.:
I created the player (Buster Posey, Catcher) in c.create_player. I want to update this player to reflect his new position, e.g. (Buster Posey, FirstBaseman) through the method update_player.
Afterwards, I want to delete the player altogether by using the method delete_player.
Right now, when I use the current method that is defined for update_player, I get the created_player and the updated_player... python prints:
Buster Posey, Catcher
Buster Posey, FirstBaseman
instead of ONLY printing Buster Posey, FirstBaseman.
More code:
class Player(object):
def __init__(self, name, position):
self._name = name
self._position = position
class API(object):
playercount = 0
managercount = 0
def __init__(self):
self._team = {} #acts like a db
self._index = 0
API.playercount += 1
API.managercount += 1
##This is the CRUD method for the entity Player
def create_player(self, name, position):
new_player = Player(name, position)
self._team[self._index] = new_player
self._index += 1
print(name, position)
def retrieve_player(self, name): # need to iterate through the dictionary
for person in self._team.itervalues():
if person._name == name:
return person
print(name)
elif person._name != name:
print('Sorry, this gent does not exist.')
Tweaked your code, I think this is what you were looking for.
class Player(object):
def __init__(self, name, position):
self._name = name
self._position = position
def __repr__(self):
return "%s --> %s" % (self._name, self._position)
class API(object):
playercount = 0
managercount = 0
def __init__(self):
self._team = {} #acts like a db
self._index = 0
API.playercount += 1
API.managercount += 1
##This is the CRUD method for the entity Player
def create_player(self, name, position):
new_player = Player(name, position)
self._team[self._index] = new_player
self._index += 1
print(name, position)
def retrieve_player(self, name): # need to iterate through the dictionary
for index, person in self._team.items():
if person._name == name:
print('Found this guy.. : ' + str(name))
return person
print('No team member with name : ' + str(name))
def update_player(self, name, position):
# predicting that you are trying to update position
updated_player = self.retrieve_player(name)
if updated_player:
temp_pos = updated_player._position
updated_player._position = position
print('updated %s position from %s to %s' %( name, str(temp_pos), str(position)))
return updated_player
else:
print('No team member with name : ' + str(name))
def delete_player(self, name, position):
for index, person in self._team.items():
print(index, person._name)
if person._name == name and person._position == position:
self._team[index] = None
del person
print('Deleted team member : ' + str(name))
return 1
print('No team member with name : ' + str(name))
team = API()
team.create_player('A', 'Catcher')
team.create_player('B', 'FirstBatsman')
print(team._team)
player = team.retrieve_player('A')
print(player._name)
team.delete_player('B', 'FirstBatsman')
print(team._team)
Thanks to gsb-eng for helping me clarify the process. My issue was in attempting to change the name when I meant change the position, which requires calling the retrieve_player, I needed to iterate through the dictionary, than implementing a new position variable and value for the formerly created player.
This made it easier to clarify the delete_player method, which needed to iterate through the dictionary again and use the built-in function del.
Lastly, I forgot to use str() when printing variables.
CODE:
def update_player(self, name, position): # call retrieve () player than update
#update position of player since name doesn't change
updated_player = self.retrieve_player(name)
if updated_player:
temp_pos = updated_player._position
updated_player._position = position
print('Updated %s position from %s to %s' %( name, str(temp_pos), str(position)))
return updated_player
else:
print('No team member with name : ' + str(name))
def delete_player(self, name, position):
for index, person in self._team.items():
print(index, person._name)
if person._name == name and person._position == position:
self._team[index] = None
del person
print('Deleted team member : ' + str(name))
return 1
print('No team member with name : ' + str(name))

Categories

Resources