I'm new in python and I'm trying to dynamically create new instances in a class. So let me give you an example, if I have a class like this:
class Person(object):
def __init__(self, name, age, job):
self.name = name
self.age = age
self.job = job
As far as I know, for each new instance I have to insert, I would have to declare a variable and attach it to the person object, something like this:
variable = Person(name, age, job)
Is there a way in which I can dynamically do this? Lets suppose that I have a dictionary like this:
persons_database = {
'id' : ['name', age, 'job'], .....
}
Can I create a piece of code that can iterate over this db and automatically create new instances in the Person class?
Just iterate over the dictionary using a for loop.
people = []
for id in persons_database:
info = persons_database[id]
people.append(Person(info[0], info[1], info[2]))
Then the List people will have Person objects with the data from your persons_database dictionary
If you need to get the Person object from the original id you can use a dictionary to store the Person objects and can quickly find the correct Person.
people = {}
for id, data in persons_database.items():
people[id] = Person(data[0], data[1], data[2])
Then you can get the person you want from his/her id by doing people[id]. So to increment a person with id = 1's age you would do people[1].increment_age()
------ Slightly more advanced material below ----------------
Some people have mentioned using list/dictionary comprehensions to achieve what you want. Comprehensions would be slightly more efficient and more pythonic, but a little more difficult to understand if you are new to programming/python
As a dictionary comprehension the second piece of code would be people = {id: Person(*data) for id, data in persons_database.items()}
And just so nothing here goes unexplained... The * before a List in python unpacks the List as separate items in the sequential order of the list, so for a List l of length n, *l would evaluate to l[0], l[1], ... , l[n-2], l[n-1]
Sure, a simple list comprehension should do the trick:
people = [Person(*persons_database[pid]) for pid in persons_database]
This just loops through each key (id) in the person database and creates a person instance by passing through the list of attributes for that id directly as args to the Person() constructor.
Related
I am attempting to find the number of unique customers for each worker from a .json file. transactions["transactions"][a]["worker] will return either Ben or David, these are the only workers and have previously been defined as objects within a class called Workers. In the for loop, I want the worker's name to be assigned to the variable wrkr, and the customer's name assigned the variable cust. I then want to check if the customer is already in that worker's list of customers, if it isn't, then I will append the name of the customer to the list. If they are already in the list I want the loop to iterate to the next transaction.
Ben.customers gives the list of customers (initially none) but if I set the variable wrkr = Ben and then do wrkr.customers it doesn't it gives me the error "AttributeError: 'unicode' object has no attribute 'customers'". I can see why as it just sees wrkr as a name and looks for it within the class. But I don't know what I should do instead?
import json
with open("transactions.json", "r") as f:
transactions = json.load(f)
class Worker:
def __init__(self, name, customers):
self.name = name
self.customers = customers
David = Worker("David", [])
Ben = Worker("Ben", [])
# Find the number of unique customers for each worker
for a in range(len(transactions["transactions"])):
cust = transactions["transactions"][a]["customer"]
wrkr = transactions["transactions"][a]["worker"]
if cust in wrkr.customers:
continue
else:
wrkr.customers.append(cust)
Gives me the error "AttributeError: 'unicode' object has no attribute 'customers'"
I want to find a workers name within the for loop and then load that worker's customer list.
I'm really sorry if my question doesn't make much sense or I'm using the wrong terminology. I'm self taught and don't really know what I'm doing.
You should create a dictionary with the keys being the worker names being expected in the json and the value being the Worker objects
class Worker:
def __init__(self, name, customers=None):
self.name = name
# If you want an empty list as a default parameter you can follow this pattern
self.customers = customers or []
workers = {
'David': Worker("David"),
'Ben': Worker("Ben")
}
for transaction_details in transactions["transactions"].values():
cust = transaction_details["customer"]
# Here you can get the Worker object from the dictionary using the worker name
wrkr = workers.get(transaction_details["worker"])
# You should handle the case where the worker is not expected
if cust in wrkr.customers:
continue
else:
wrkr.customers.append(cust)
Looking at your code, I am trying to guess what your data looks like, which is not easy, so if my solution does not work, please post what your data looks like. Here is what I have in mind:
import itertools
import json
from pprint import pprint
class Worker:
def __init__(self, name, customers):
self.name = name
self.customers = customers
def __repr__(self):
return 'Worker(name={}, customers={})'.format(self.name,
self.customers)
with open('transactions.json') as file_handle:
data = json.load(file_handle)
#workers is a dictionary where key=worker name, value=Worker object
workers = {}
for transaction in data['transactions']:
worker_name = transaction['worker']
customer = transaction['customer']
#Create a new worker object if needed
workers.setdefault(worker_name, Worker(worker_name, set()))
#Build the customers list
workers[worker_name].customers.add(customer)
pprint(workers)
Output:
{'Ben': Worker(name=Ben, customers={'Lisa', 'Janet', 'Alex'}),
'David': Worker(name=David, customers={'Jason', 'Anna'})}
Notes
Instead of having two variables Ben and David, I created a dictionary named workers for easy look up. The keys are the name of the workers and the values the Worker objects.
From your code, you have a test to make sure not to add the same name to the customers list. This tells me that you want a set, not a list. Using a set with simplify your logic because you don't have to deal with if/else statement.
The workers.setdefault() call deserve some explanation if you are not familiar with it. Here is what the documentation said:
setdefault(key[, default])
If key is in the dictionary, return its value. If not, insert key with a value of default and return default. default defaults to None.
What this means is let say that the key 'Ben' is not in the dictionary, the setdefault method will add a new key/value to the dictionary. If the key is already in the dictionary, the setdefault does not do anything, but return the current value. Thus the line:
workers.setdefault(worker_name, Worker(worker_name, set()))
is equivalent to:
if worker_name not in workers:
workers[worker_name] = Worker(worker_name, set())
Basically, I want to store objects with two attributes (name, score) in a list. From there I want to find the object in the list with the highest score, and be able to access the name of that object. So if 50 is determined to be the highest score in the list, I wan't to be able to see that this score belongs to "wages".
Here is my starting code where I create the objects with set attributes (for testing).
Storing the objects in the list works just fine, and I can access individual attributes in a for-loop (for i in a_list: print(i.score)) for example. But when it comes to accessing the maximum score and get the name associated with that object I am at a loss.
class Scoreboard:
def __init__(self, name, score):
self.name = name
self.score = int(score)
t1 = Scoreboard("wages", 50)
t2 = Scoreboard("todo", 15)
t3 = Scoreboard("invoice", 36)
a_list = []
a_list.append(t1)
a_list.append(t2)
a_list.append(t3)
I tried using max() but that returns an error unless I only store the score value (ex. t1.score) in the list. If I do that I can't find a way to access the name attribute of that object.
Try this:
m = max(a_list, key=lambda s: s.score)
That should use the score attribute of each object in the list as the key when finding the max.
Python has no way of knowing what Scoreboard is, and that it is supposed to use the .score attribute for calculating maxs or mins. As in the comments, a dictionary seems more appropriate. If you want to go on with classes you need to ask python to give you the name of the instance with score equal to max(scores). Something like:
[(t.name, t.score) for t in a_list if t.score == max(t.score for t in a_list)]
>>[('wages', 50)]
I am new to Python and currently searching for some internship or a job. I am currently working on a program in Python which reads a file that contains data in this shape:
Id;name;surname;age;gender;friends;
Id and age are the positive integers,
gender can be "male" or "female",
and friends is an array of numbers, separated by comma, which represent the Id's of persons who are friends with the current person. If Person1 is a friend to a Person2, it must work vice versa.
As you can see in the above example, attributes of a "Person" are separated by semicolon, and the trick is that not every person has every attribute, and of course, they differ by the number of friends. So, the first part of the task is to make a program which reads a file and creates a structure which represents a list of persons with the attributs mentioned above. I have to make a search for those persons by Id.
The second part is to make a function with two arguments (Id1, Id2) which returns True if a person with Id2 is a friend to a person with Id1. Otherwise, it returns false.
I have some ideas on my mind, but I am not sure how to realize this, since I don't know enough about Python yet. I guess the best structure for this would be a dictionary, but I am not sure how to load a file into it, since the attributes of all persons are different. I would be greatful for any help you can offer me.
Here is my attempt to write the code:
people = open(r"data.txt")
class People:
id = None
name = ''
surname = ''
age = None
gender = ['male', 'female']
friends = []
#def people(self):
# person = {'id': None,
# 'name': '',
# 'surname': '',
# 'age': None,
# 'gender': ['male', 'female'],
# 'friends': []
# }
# return person
def community(self):
comm = [People()]
return comm
def is_friend(id1, id2):
if (id1 in People.friends) & (id2 in People.friends):
return True
people.close()
Your question is too broad imho, but I'll give you a few hints:
the simplest datastructure for O(n) key access is indeed a dict. Note that a dict needs immutable values as keys (but that's fine since your Ids are integers), but can take anything as values. but that only works for (relatively) small datasets since it's all in memory. If you need bigger datasets and/or persistance, you want a database (key:value, relational, document, the choice is up to you).
Python has classes and computed attributes
In Python, the absence of a value is the None object
there's a csv files parser in the standard lib.
Now you just have to read the doc and start coding.
[edit] wrt/ your code snippet
class People:
id = None
name = ''
surname = ''
age = None
gender = ['male', 'female']
friends = []
Python is not Java or PHP. What you defined above are class attributes (shared by all instances of the class), you want instance attributes (defined in the __init() method). You should really read the FineManual.
Also if you're using Python 2.7.x, you want your classes to inherit from object (historical reasons).
So your Person class should look something like this:
class Person(object):
def __init__(self, id, name, surname, age, gender, friends=None):
self.id = id
self.name = name
self.surname = surname
self.age = age
self.gender = gender
self.friends = friends or []
And then to create a Person instance:
person = Person(42, "John Cleese", "Archie Leach", 77, "male", [11, 1337)])
def is_friend(id1, id2):
if (id1 in People.friends) & (id2 in People.friends):
return True
A few points points here:
First: you either want to rename this function are_friends or make it a method of the Person class and then only pass a (single) Person instance (not an 'id') as argument.
Second: in Python, & is the bitwise operator. The logical "and" operator is spelled, well, and.
Third: an expression has a truth value by itself, so your if statement is redundant. Whenever you see something like:
def func():
if <some expression>:
return True
else:
return False
you can just rewrite it as :
def func():
return <some expression>
Or if you want to ensure func returns a proper boolean (True or False):
def func():
return bool(<some expression>)
I'll stop here because I don't intend to teach you how to program. You obviously need to do at least the full official Python tutorial, and possibly some complete beginner tutorial too.
I have queryset of people:
people = Person.objects.all()
and I have a list un_people = [] - meaning a list of people with unique name.
So, there can be more than one person with the same name. i want to filter for this and then insert into list so that list only contains person objects with unique name.
I tried:
for person in people:
if person.name in un_people:
#... ?
but in list, there are objects, not names. how can I check for objects with same name and then insert into list?
Use a dict to do the uniqueness, then take the values, eg:
uniq_names = {person.name:person for person in people}
uniq_people = uniq_names.values() # use list(unique_names.values()) for Py 3.x
You can use set data structure:
un_people = set(people)
If your elements are not hashable as, JonClemens, suggests you can build a list of names first:
un_people = set([p.name for p in people])
I have a class which looks like this.
class CharInStageList(object):
def __init__(self, charid, charname) :
self.charid = charid
self.charname = charname
into this class I would like to add lists that I have.
I know how to do it the normal way
charOne = CharInStageList(1,'Tim')
charTwo = CharInStageList(2,'Struppi')
that's not a problem what I actually want to do is to add them by using a loop.
I get my data in this form
((1,'Tim'),(4,'Struppi'))
or
((1,'Tim'),(4,'Struppi'),(5,'Nami'),(6,'Luffy'))
the amount of characters I have in the scene is always different.
what I imagined would be a loop like this
charlist = ((1,'Tim'),(4,'Struppi'))
for char in charlist
objname = CharInStageList(char[0],char[1])
something like this
I want the objname to change by itself for every object I add to the class.
How can I get this effect?
I can only use python 2.6.6 for this since it's the maya 2013 python
Edit:
Thx #silas-ray #chepner #theodox I looked into Dicts a bit more and that's pretty much what I need
I use a modified version of #chepner method on it.
object_dict = dict( (y, CharInStageList(x,y)) for x,y in data )
Works like a charm
My testcode looks like this
import maya.cmds as cmds
dicttest = {}
def getdata ():
global dicttest
data = ((1,'Tim'),(4,'Struppi'),(5,'Nami'),(6,'Luffy'))
dicttest = dict( (y,(x,y)) for x,y in data )
getdata()
def printtest() :
for char in dicttest:
print dicttest[char]
printtest()
dicttest.clear()
I would have liked to comment in your answers with code examples but I can't get that to work there.
Objects are not added to a class. Instead, you can create a list of objects which are all instances of the same class, using a list comprehension and taking advantage of the *args syntax:
data = ((1,'Tim'),(4,'Struppi'),(5,'Nami'),(6,'Luffy'))
object_list = [ CharInStageList(*x) for x in data ]
Perhaps you want a dictionary instead:
object_dict = dict( (y, CharInStageList(x,y)) for x,y in data )
(Note that CharInStageList is a poor name for the class, because it's not a list; it encapsulates a single character.)
If you really want CharInStateList to be a collection of characters, try something like this, which is just a thin wrapper around a dictionary:
# Your former CharInStageList
class Character(object):
def __init__(self, charid, charname) :
self.charid = charid
self.charname = char name
class OnStageCharacters(object):
def __init__(self):
self.characters = dict()
# Index on-stage characters using their charid
def add(self, character):
self.characters[character.charid] = character
on_stage = OnStageCharacters()
for id, name in data:
on_stage.add( Character(id, name) )
You can't (at least not without hacking at locals/globals, which is generally not a good idea) change the name you are assigning to dynamically like that. You can, however, insert them in to a dictionary where the keys are your dynamically generated names.
characters = {}
for char_data in char_list:
characters[char_data[1]] = CharInStageList(*char_data)
Though if all your character objects are storing is name and id, it might make more sense to simplify the whole thing and just create mapping dictionaries rather than objects.
character_names_by_id = dict(char_data)
character_ids_by_name = dict((name, id) for id, name in char_data)
#chepner's answer is a great one if you can use the *args form to fill out your class instances.
If you're just asking the most efficient way to do this from a loop, remember you can have iterate over the parts of a tuple together:
data = ((1,'Tim'),(4,'Struppi'),(5,'Nami'),(6,'Luffy'))
class_data = (CharInStageList(id, name) for id, name in data) # "for id, name" will yield two vals
You can also use map, which is very common for doing bulk data translations. A common way to do it is with a lambda so you can write it clearly:
to_char = lambda k: CharInStageList(k[0], k[1])
class_data = map(to_char, data)
If you're doing something as simple as your example, you might not want to bother with your own class. the namedtuple is a great data structure for creating tuples that are easy to work with. It also means you can use positional or named args interchangeably, just as in #chepner's *args version:
StageListChar = namedtuple('CharInStageList', ['id', 'name'])
class_data = map(StageListChar , data)