python adding a class object to a list - python

Im trying to create an empty instance of my class object and add it to a list, however, as soon as i try to create the object add new data i get an error for it as seen below:
error
Traceback (most recent call last):
File "pagerduty.py", line 96, in <module>
UserData = User()
TypeError: 'dict' object is not callable
code
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])
OnCallData = []
for User in objPolicyData['users']:
UserData = User()
UserData.Name = User['name']
UserData.Mobile = UserMobile = getUserMobile(User['id'])
for OnCall in User['on_call']:
UserPolicy = OnCall['escalation_policy']
PolicyName = UserPolicy['name']
if PolicyName.lower().find('test') == -1:
UserData.Policy = PolicyName
UserData.Level = OnCall['level']
UserData.StartDate = getDate(OnCall['start'])
UserData.EndDate = getDate(OnCall['end'])
UserData.StartTime = getTime(OnCall['start'])
UserData.EndTime = getTime(OnCall['end'])
OnCallData.append(UserData)

in your for scope, the User identifier is the iterated value from objPolicyData['users'] (as you used it in UserData.Name = User['name'])
you need to use a diffierent name for the iteration.
something like that:
for userI in objPolicyData['users']:
UserData = User()
UserData.Name = userI['name']
UserData.Mobile = UserMobile = getUserMobile(userI['id'])
for OnCall in userI['on_call']:
UserPolicy = OnCall['escalation_policy']
PolicyName = UserPolicy['name']
if PolicyName.lower().find('test') == -1:
UserData.Policy = PolicyName
UserData.Level = OnCall['level']
UserData.StartDate = getDate(OnCall['start'])
UserData.EndDate = getDate(OnCall['end'])
UserData.StartTime = getTime(OnCall['start'])
UserData.EndTime = getTime(OnCall['end'])
Note that using the conventions could prevent this bug for you. meaning, starting the name of a variable with lower case letter (user) and a class with capital letter (User).
this is relevant for most of your variables names

Both your class and the variable you are iterating over is called User. Change one of them to a different name.
By convention in python variable names are lowercase. So I suggest the following change:
for user in objPolicyData['users']:
user_data = User()
user_data.name = User['name']
user_data.mobile = UserMobile = getUserMobile(User['id'])
for on_call in User['on_call']:
user_policy = on_call['escalation_policy']
policy_name = user_policy['name']
if policy_name.lower().find('test') == -1:
user_data.policy = policy_name
user_data.level = on_call['level']
user_data.start_date = get_date(on_call['start'])
and so on. This follows the naming conventions in pep8. This will also take care of your error.
Of course if there is already an established style guide you should follow it and decide for a different name of User (the iteration variable).

Related

How can I access attributes in class instances held inside an instance of another class?

I am creating a stock screener with Python. My data looks like this. Because of the format of the data, I am trying to use nested classes (I think that's what I am doing) to be able to reference the data. Data Image
Here is the code for my classes:
class FieldClass:
def __init__(self, parts):
if parts[2].replace(" ", "") == 'Price':
self.price = YearClass(parts)
elif parts[2].replace(" ", "") == 'Revenues':
self.rev = YearClass(parts)
elif parts[2].replace(" ", "") == 'OperatingIncome':
self.opincome = YearClass(parts)
elif parts[2].replace(" ", "") == 'OperatingMargin':
self.opmargin = YearClass(parts)
elif parts[2].replace(" ", "") == 'NetProfitMargin':
self.profitmargin = YearClass(parts)
class YearClass:
# creates a class whose objects/attributes are a dict of each year of data
def __init__(self, parts):
self.years_data = dict()
self.years_data[2009] = (parts[3])
self.years_data[2010] = (parts[4])
self.years_data[2011] = (parts[5])
self.years_data[2012] = (parts[6])
self.years_data[2013] = (parts[7])
self.years_data[2014] = (parts[8])
self.years_data[2015] = (parts[9])
self.years_data[2016] = (parts[10])
self.years_data[2017] = (parts[11])
self.years_data[2018] = (parts[12])
self.years_data[2019] = (parts[13])
Here is how I call/reference the classes in my code:
def data_prep(fname):
tkr_list = []
data_dict = dict()
with open(fname) as f_in:
f_in.readline()
for lines in f_in:
parts = lines.strip('\n').split(",")
tkr = parts[1]
key_cat = parts[2].replace(" ", "")
if tkr not in tkr_list:
tkr_list.append(tkr)
for tkr in tkr_list:
data_dict[tkr] = FieldClass(parts)
var = data_dict['FLWS'].opincome.years_data[2010]
Here is the error I am getting:
AttributeError: 'FieldClass' object has no attribute 'opincome'
Could someone please tell me what I am doing wrong?
You do not have defaults set for your attributes, and they will only be set if the correct condition is met - as such, could it be that your input data does not have an OperatingIncome set and therefore your class is not having that attribute set / made to exist?
Your error as nothing to deal with nested class.
The issue is on your init function in class FieldClass.
You're setting your class attributes on conditions so if the condition is not met your class won't have this attribute.
In this case it's opincome attribute
Here is your options:
set default attributes
remove these conditions
change your code in a way that FieldClass loads all your necessary attributes (price, rev, opincome, ...). I recommand you this option

update the same group if group name clashes

How can i update the same group if the name of group user wants to create matches with already created group? If i want to update instead of showing error where should i work on? Is it on validate function or create function?
Here is my serializer
class DeviceGroupSerializer(serializers.ModelSerializer):
id = serializers.UUIDField(source='token', format='hex', read_only=True)
devices = DeviceIdSerializer(many=True)
class Meta:
model = DeviceGroup
fields = ['id','name', 'devices',]
def validate(self, data):
errors = {}
try:
name = data['name']
if not bool(name):
#empty or null
errors['name'] = 'Name cannot be empty'
except KeyError:
if not (self.instance and bool(self.instance.name)):
errors['name'] = 'Name is required'
if len(data.get('devices', [])) == 0:
errors['devices'] = 'Device(s) should be specified.'
if bool(errors):
raise serializers.ValidationError(errors)
return data
def create(self, validated_data):
# for create - there is always name; we have already checked that in validation
# TODO Further check for group-name clash - if yes, update the same group
owner = validated_data['owner']
name = validated_data['name']
group = DeviceGroup.objects.create(owner=owner, name=name)
tokens = [d['token'] for d in validated_data['devices'] ]
BaseDevice.objects.filter(token__in=tokens, owner=owner).update(group=group)
return group
def update(self, instance, validated_data):
# for update - there may or may not be name
# if it does, it refers rename
owner = validated_data['owner']
name = validated_data.get('name', None)
if not name is None:
instance.update(name=name)
tokens = [d['token'] for d in validated_data['devices'] ]
BaseDevice.objects.filter(token__in=tokens, owner=owner).update(group=instance)
return instance
You want update_or_create():
A convenience method for updating an object with the given kwargs,
creating a new one if necessary. The defaults is a dictionary of
(field, value) pairs used to update the object.
Based on what you've shared, this would look something like the following, assuming you want to update the owner on DeviceGroup, if a DeviceGroup with the given name already exists:
def create(self, validated_data):
# for create - there is always name; we have already checked that in validation
# TODO Further check for group-name clash - if yes, update the same group
owner = validated_data['owner']
name = validated_data['name']
# created is a boolean telling us if a new DeviceGroup was created
group, created = DeviceGroup.objects.update_or_create(name=name, defaults={'owner': owner})
tokens = [d['token'] for d in validated_data['devices'] ]
BaseDevice.objects.filter(token__in=tokens, owner=owner).update(group=group)
return group

Python :TypeError: this constructor takes no arguments

When the user enters an email address, and the program reads the email and display it according to its criteria (e.g yeo.myy#edu.co), like criteria:
username is yeo.myy
domain is edu.co
I know its something to do with the "#".
this is the code
class Email:
def __int__(self,emailAddr):
self.emailAddr = emailAddr
def domain(self):
index = 0
for i in range(len(emailAddr)):
if emailAddr[i] == "#":
index = i
return self.emailAddr[index+1:]
def username(self):
index = 0
for i in range(len(emailAddr)):
if emailAddr[i] == "#" :
index = i
return self.emailAddr[:index]
def main():
emailAddr = raw_input("Enter your email>>")
user = Email(emailAddr)
print "Username = ", user.username()
print "Domain = ", user.domain()
main()
this is the error I got:
Traceback (most recent call last):
File "C:/Users/Owner/Desktop/sdsd", line 29, in <module>
main()
File "C:/Users/Owner/Desktop/sdsd", line 24, in main
user = Email(emailAddr)
TypeError: this constructor takes no arguments
def __int__(self,emailAddr):
Did you mean __init__?
def __init__(self,emailAddr):
You're also missing a couple selfs in your methods, and your returns are improperly indented.
def domain(self):
index = 0
for i in range(len(self.emailAddr)):
if self.emailAddr[i] == "#":
index = i
return self.emailAddr[index+1:]
def username(self):
index = 0
for i in range(len(self.emailAddr)):
if self.emailAddr[i] == "#" :
index = i
return self.emailAddr[:index]
Result:
Username = yeo.myy
Domain = edu.co
Incidentally, I recommend partition and rpartition for splitting a string into two pieces on a given separator. Sure beats keeping track of indices manually.
def domain(self):
return self.emailAddr.rpartition("#")[2]
def username(self):
return self.emailAddr.rpartition("#")[0]
This error may happen if you type def _init_ with a single underline instead of def __init__ with double underlines before and after init.
class Employee:
def __init__(self,Name,Age,Salary,Gender):
self.Name = Name
self.Age = Age
self.Salary= Salary
self.Gender = Gender
def show_employee_deatils(self):
print("Name of the employee is ",self.Name)
print("Age of the employee is ",self.age)
print("Salary of the employee is ",self.salary)
print("gender of the employee is ",self.gender)
e1 = Employee('Shubham',25,25000,'male')
e1. show_Employee_deatils( )

Generate list of instances in Python [duplicate]

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()

Class design: Last Modified

My class:
class ManagementReview:
"""Class describing ManagementReview Object.
"""
# Class attributes
id = 0
Title = 'New Management Review Object'
fiscal_year = ''
region = ''
review_date = ''
date_completed = ''
prepared_by = ''
__goals = [] # List of <ManagementReviewGoals>.
__objectives = [] # List of <ManagementReviewObjetives>.
__actions = [] # List of <ManagementReviewActions>.
__deliverables = [] # List of <ManagementReviewDeliverable>.
__issues = [] # List of <ManagementReviewIssue>.
__created = ''
__created_by = ''
__modified = ''
__modified_by = ''
The __modified attribute is a datetime string in isoformat. I want that attribute to be automatically to be upated to datetime.now().isoformat() every time one of the other attributes is updated. For each of the other attributes I have a setter like:
def setObjectives(self,objectives):
mro = ManagementReviewObjective(args)
self.__objectives.append(mro)
So, is there an easier way to than to add a line like:
self.__modified = datetime.now().isoformat()
to every setter?
Thanks! :)
To update __modified when instance attributes are modified (as in your example of self.__objectives), you could override __setattr__.
For example, you could add this to your class:
def __setattr__(self, name, value):
# set the value like usual and then update the modified attribute too
self.__dict__[name] = value
self.__dict__['__modified'] = datetime.now().isoformat()
Perhaps adding a decorator before each setter?
If you have a method that commits the changes made to these attributes to a database (like a save() method or update_record() method. Something like that), you could just append the
self.__modified = datetime.now().isoformat()
just before its all committed, since thats the only time it really matters anyway.

Categories