Python create instance of class using string manipulation - python

I wrote Class and created two lists. The first for the users and the second for the user-attributes.
I would now like to loop trough the two lists in order to create multiple Class instances with the respective data.
class Employee:
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.pay = pay
self.email = first + '.' + last + '#company.com'
emp_1 = Employee('Corey', 'Schafer', '50000')
emp_2 = Employee('Test', 'User', '60000')
by printing print(amp_1.email) i can create the instance of the class:
will print
Corey.Schafer#company.com
Now i dont want to write it out manually so i want to loop trough it:
for user in users:
for user_atr in user_atrs:
print(user + '.' + user_atr)
will print:
empy_1.first
empy_1.last
empy_1.pay
empy_1.email
empy_2.first
empy_2.last
empy_2.pay
empy_2.email
Instead of:
Corey
Schafer
50000
Corey.Schafer#email.com
Test
User
60000
Test.User#email.com
How can i use that loop to actually create the instance of the class and not just the blueprint?

Basic Solution
a list of string of the users : ['emp_1', 'emp_2']
a list of string that are attributs name ['first', 'last', 'pay', 'email']
Then use the builtin method globals() to get the variable and getattr(obj, name, default) but that isn't nice and requires to type variable names
emp_1 = Employee('Corey', 'Schafer', 50000)
emp_2 = Employee('Test', 'User', '60000')
for user in ['emp_1', 'emp_2']:
for user_atr in ['first', 'last', 'pay', 'email']:
print(getattr(globals()[user], user_atr))
Better Solution
a list of Employee instances : [emp_1, emp_2]
Access object properties with __dict__ (key is name, value are property's value)
for user in [emp_1, emp_2]:
for user_atr in user.__dict__.values():
print(user_atr)
Corey
Schafer
50000
Corey.Schafer#company.com
...
To read both name and value at the same time
for user in [emp_1, emp_2]:
for user_atr in user.__dict__.items():
print(user_atr)
('first', 'Corey')
('last', 'Schafer')
('pay', 50000)
('email', 'Corey.Schafer#company.com')
...

class Employee:
def __init__(self, first, last, pay):
self.first = first
self.last = last
self.pay = pay
self.email = first + '.' + last + '#company.com'
emp_1 = Employee('Corey', 'Schafer', '50000')
emp_2 = Employee('Test', 'User', '60000')
users = {"emp_1": emp_1,
"emp_2": emp_2}
for user in users.values():
for attribute in list(user.__dict__.keys()):
print(f"{name}.{attribute}")
This will do that for you by storing the users in a dictionary with the name of the user as the dictionary key and then using the __dict__ attribute from the Employee class to get a dictionary of all atributes and then printing the name of each attribute from that dictionary.
If you are trying to print the value of each of these attributes then it can be changed to the following:
users = {"emp_1": emp_1,
"emp_2": emp_2}
for name, user in users.items():
for attribute in list(user.__dict__.keys()):
print(getattr(user,attribute))
Using this method means that any more attributes added to the employee class will be printed also.

Related

Write a function to reduce duplicate code

I have two very similar for loops, I want to have an inner function to reduce the duplicate codes, they look like this:
team_members = TeamMember.objects.all()
managers = Manager.objects.all()
for m in managers:
name = f"{m.name.first_name} {m.name.last_name}"
//reset of the code are the same
for t in team_members:
name = f"{t.member.first_name} {t.member.last_name}"
//reset of the code are the same
So the problem is managers and team_members querysets have different field names for people's names.
If I want to write an inner function, how to solve the different field names?
you could pass in m.name and t.member to that function which would allow it to access that item.
for m in managers:
func(m.name)
for t in team_members:
func(t.member)
def func(member):
name = f'{member.first_name} {member.last_name}
#Rest of code
Here is the inner function which will take objs as input and fetch the values based on objects attribute.
def inner_fun(objs):
for obj in objs:
if hasattr(obj, 'name'):
name_obj = getattr(obj, 'name')
else:
name = getattr(obj, 'member')
name = f"{name_obj.first_name} {name_obj.last_name}"
return name
team_members = TeamMember.objects.all()
managers = Manager.objects.all()
team_name = inner_fun(team_members)
manager_name = inner_fun(managers)
Tom Karzes solution in code:
team_members = TeamMember.objects.all()
managers = Manager.objects.all()
for group, attr_name in zip([team_members, managers], ['name', 'member']):
for person in group:
name = f"{getattr(person, attr_name).first_name} {getattr(person, attr_name).last_name}"

Pytest - how to stub member variable

I apologise - this is my first attempt at using pytest or any python testing library, but I have done a small amount of JUnit so am vaguely familiar with the principles.
Basically, the class I want to test has a couple of member variables that I want to stub. Specifically, I only need some customer details. I access this under the OrderController class in its class variable 'orders' (a list of dictionaries with purchase id as key and order objects as values). When I get this order object I would like to access the customer attribute which is comprised of their name and address - this address attribute is another member variable.
Below is the address_label.py module (I'm sorry about the comments - it is for University)
"""
--------------------------------------------------------------------------------------------
title : address_label.py
description : Formats order data for creation of address labels in the pdf.py module.
python_version : 3.7.9
--------------------------------------------------------------------------------------------
"""
from .order_controller import OrderController
from .pdf import Pdf
class AddressLabel:
"""
A class for formatting data to be input into address label pdf.
...
Attributes
----------
order_controller : OrderController
member variable to access order instances
pdf : Pdf
member variable to invoke writing of pdf
Methods
-------
create_address_label(orders_selected):
Create strings to be output in pdf
"""
def __init__(self):
"""
Constructs all the necessary attributes for the AddressLabel object.
"""
self._order_controller = OrderController(None)
self._pdf = Pdf()
def create_address_label(self, orders_selected):
"""
For each of the orders selected with checkboxes will find the data for that order
and format is suitable for the pdf module.
Parameters:
orders_selected: an array of the row data from each row checked with a checkbox
(each item is a string).
"""
for index, order in enumerate(orders_selected):
order_id = int(order[0]) - 1
order_obj = self._order_controller.orders['order_' + order[0]]
address = [order_obj.customer.first_name + ' ' + order_obj.customer.last_name,
order_obj.customer.address.line_one, order_obj.customer.address.city]
self._pdf.write_address_label(address, order_id, index)
return address, order_id, index
This is what I have so far for test_address_label.py, but I notice that it is still contacting the main OrderController class and therefore failing - how can I stop this?
import pytest
from main.business_logic.address_label import AddressLabel
class Address:
def __init__(self, line_one, line_two, city):
self.line_one = line_one
self.line_two = line_two
self.city = city
class Customer:
def __init__(self, address, first_name, last_name):
self.address = address
self.first_name = first_name
self.last_name = last_name
class Order:
def __init__(self, customer):
self.customer = customer
class OrderController:
orders = {
'order_1': Order(customer=setup_customer())
}
def __init__(self, x):
pass
#staticmethod
def setup_customer():
def setup_address():
return Address(line_one='Test Line One',
line_two='Test Line Two', city='Test City')
address = setup_address()
return Customer(address=address, first_name='Test First Name', last_name='Test Last Name')
#pytest.fixture
def _order_controller():
return OrderController()
def test_address_label(_order_controller):
address_label = AddressLabel()
orders_selected = [['1', 'Test Name', '2021-03-12', 'Status', '£200']]
scenario_one_address = ['Test First Name Test Last Name', 'Test Line One', 'Test City']
address_label_contents = address_label.create_address_label(
orders_selected)
assert address_label_contents == (scenario_one_address, 1, 0)
In any case, if anyone had any good resources to learn this from that'd be great - I've read a lot of tutorials but they all use such elementary examples that don't apply to a lot of my use cases...
Thank you in advance!

Printing classes in Python with multiple values

I have spent a good while searching through this website so I hope this question hasn't been asked before - apologises if it has. I'm learning classes for the first time and I'm making a class with multiple users (could be 50+ but for now, I just have 2 in my example). What I'm trying to do is have certain information about users/employees and be able to print them all in one go... in a way that isn't a complete eyesore! This is what I have attempted:
class User:
def __init__(self, user_id, first, last, address):
self.user_id = user_id
self.first = first
self.last = last
self.address = address
self.email = first + '.' + last + '#python.com'
def all_users(self):
print()
print('User ID: {} First Name: {} {} {} {}'.format(self.user_id, self.first, self.last, self.address, self.email))
print()
user_1 = User(123, 'Kim', 'N', 'London')
user_2 = User(312, 'Chris', 'E', 'Japan')
print(all_users(User))
This is the error message that I am receiving:
print('User ID: {} First Name: {} {} {} {}'.format(self.user_id, self.first, self.last, self.address, self.email))
AttributeError: type object 'User' has no attribute 'user_id'
Thanks in advance for any help or guidance.
Sounds like you want the User class to contain a list of all users.
This is called a class variable because it is attached to the class itself, instead of being attached to a particular instance of the class.
Example code:
class User(object):
users = []
def __init__(self, first, last):
self.first = first
self.last = last
# now that this instance is fully initialized, add it
# to the master list of users
User.users.append(self)
def __str__(self):
return '{} {}'.format(self.first, self.last)
#staticmethod
def all_users():
for user in User.users:
print (user)
user_1 = User('Kim', 'Smith')
user_2 = User('Chris', 'Jones')
User.all_users()
You should probably implement the __str__ or __repr__ special methods, which are designed to print human readable and "official" representations of the class instances, respectively
class User():
...
def __str__(self):
attrs = ['{}={}'.format(k, repr(v)) for k, v in self.__dict__.items()]
return '{}({})'.format(self.__class__.__name__, ', '.join(attrs))
Then it would look like this
>>> user = User('123', 'John', 'Doe', 'USA')
>>> print(user)
User(user_id='123', first='John', last='Doe', address='USA', email='John.Doe#python.com')

Python Dynamic Object Creation [duplicate]

This question already has answers here:
How do I create variable variables?
(17 answers)
Closed 8 years ago.
I have tried using list and/or dictionaries to store the objects that I create, but my attempts have failed.
A reasons to use a list or a dictionary is to have python keep a reference to the object so as prevent the garbage collection component from discarding our object. My issues seems to be more with the initialization of the object itself, and with naming the object.
My script takes data out of a database and then automatically creates objects for each item in the list, in our case, employees.
Here is my class:
class Employee:
empCount = 0
def __init__(self, employee_id, name, age):
self.employee_id = employee_id
self.name = name
self.age = age
Employee.empCount += 1
def display_employee_count(self):
print("Total Employee %d" % Employee.empCount)
def display_employee(self):
print("Employee ID : ", self.employee_id,
" First name : ", self.name,
' Age : ', self.age)
I know that to create an object I would call
joe1883 = Employee('joe1883', 'Joe', 21)
To display the objects attributes I would
joe1883.display_employee()
which yields:
Employee ID : joe1883 First name : Joe Age : 21
My question is, how do I do this through a loop ?
# Data set has, employee id, first name, and age
my_list = [['joe1883', 'joe', 21],
['bob1492', 'bob', 22],
['frank1889','frank',34]]
for names in my_list:
employee_id = names[0]
name = names[1]
age = names[2]
#print("Employee ID : " + employee_id + " First name : " + name + ' Age : ' + str(age))
# This is the line that creates my error !
employee_id = Employee(employee_id, name, age)
If I insert this line into the loop statement
employee_id = Employee(employee_id, name, age)
And then call
joe1883 = Employee('joe1883', 'Joe', 21)
I get a #NameError: name 'joe1883' is not defined.
You need to keep a reference to an object around not just so it isn't garbage-collect, but also so you can reference it. The reference can be by giving it a variable name, like joe1883, or it can be by storing the object in a container, like a list or dictionary.
If you put it in a list, each element in the list can be reference with an integer index, such as employees[42]. To find an employee in such a container you will need to search through potentially all of them, by index, until you find the one you want.
If you want to find an employee quickly given their id, you should store them in a dictionary instead, then you could do something like employees['joe1883'] to directly access one of them.
Since you don't know kind of container an Employee instance will be put in, if any, it makes little sense to keep an empCount in the class. If you put a bunch of them in a list or dictionary, and ever need to know how many, you can find that by using the built-in len() function on the container object, i.e. len(employee_list) or len(employee_dict).
class Employee:
def __init__(self, id, name, age):
self.id = id
self.name = name
self.age = age
def display(self):
print("Employee ID : ", self.id,
" First name : ", self.name,
' Age : ', self.age)
# create a single employee object and name it joe1883
joe1883 = Employee('joe1883', 'Joe', 21)
joe1883.display()
# now create a list of employees by converting dataset
# my_list into a list of Employee instances named employees
# dataset
my_list = [
['joe1883', 'joe', 21],
['bob1492', 'bob', 22],
['frank1889','frank', 34],
]
employees = [Employee(item[0], item[1], item[2]) for item in my_list]
# display each employee in list
for employee in employees:
employee.display()
# or you could do it this (non-pythonic) way:
for i in range(len(employees)):
employees[i].display()
# to print an employee with a given id
# you'd need to search for it in the list
for employee in employees:
if employee.id == 'joe1883':
employee.display()
break # found, so stop loop
# Here's how to create a dictionary of employees by converting dataset
# my_list into Employee instances keyed by their id
employees = {item[0]: Employee(item[0], item[1], item[2]) for item in my_list}
# look one of them up
if 'joe1883' in employees:
employees['joe1883'].display()
If I understand your question correctly, you are trying to create a set of objects-employees based on the list with the arguments for each of the objects.
I modified/simplified your code a little bit and this is working fine:
class Employee:
empCount = 0
def __init__(self, employee_id, name, age):
self.employee_id = employee_id
self.name = name
self.age = age
Employee.empCount += 1
def display_employee_count(self):
print("Total Employee %d" % Employee.empCount)
def display_employee(self):
print("Employee ID : {0} First name : {1} Age : {2}".format(self.employee_id, self.name,self.age))
my_list = [('joe1883', 'joe', 21), ('bob1492', 'bob', 22), ('frank1889', 'frank', 34)] # in this format arguments will be easier to pass as parameters to your class constructor
list_of_employees = [] # list where you can put created objects
for emp in my_list:
list_of_employees.append(Employee(*emp)) # now you just pass your tuple of arguments like that
for emp in list_of_employees: #looping through the list with objects you created and printing results
emp.display_employee()
#eduard You are doing 2 things wrong.
1) the indentation is bad, so the methods of the class Employee are not inside the class.
2) You are not storing the objects anywhere.
Id suggest you to do it in this way:
employ_dic={}
class Employee:
def __init__(self, employee_id, name, age):
self.employee_id = employee_id
self.name = name
self.age = age
employ_dic[employee_id]=self
def display_employee(self):
print '''Employee ID: {0}, First Name: {1}, Age: {2}'''.format(self.employee_id, self.name, self.age)
lists = [['joe1883', 'joe', 21],['bob1492', 'bob', 22],['frank1889','frank',34]]
for list in lists:
employee_id = Employee(list[0], list[1], list[2])
employ_dic['joe1883'].display_employee()
# to display the count:
print len(employ_dic)

How do I assign a variable to an object name?

Tried the following, where "objectname" contains a string name, to be assigned on creation of an object.
for record in result:
objectname = 'Customer' + str(record[0])
print objectname
customername = str(record[1])
objectname = Customer(customername)
Where Customer is a class.
In my test, this loop runs twice printing "objectname" as Customer1 and Customer2, yet creates 2 objects, but the objects are called "objectname" (it overwrites each loop), opposed to the 2 unique objects Customer1 or Customer2.
Its simply not assigning strings(customer1,2) inside the variable, but purely the variables name.
I've tried assigning strings to the object name, but that gives a syntax error
Surely this must be done all the time, thanks for your help in advance.
Instead of using a new variable for each customer you could store your object in a Python dictionary:
d = dict()
for record in result:
objectname = 'Customer' + str(record[0])
customername = str(record[1])
d[objectname] = Customer(customername)
print d
An example of objects stored in dictionaries
I just could'nt help my self writting some code (more than I set out to do). It's like addictive. Anyway, I would'nt use objects for this kind of work. I probably would use a sqlite database (could be saved in memory if you want). But this piece of code show you (hopefully) how you can use dictionaries to save objects with customer data in:
# Initiate customer dictionary
customers = dict()
class Customer:
def __init__(self, fname, lname):
self.fname = fname
self.lname = lname
self.address = None
self.zip = None
self.state = None
self.city = None
self.phone = None
def add_address(self, address, zp, state, city):
self.address = address
self.zip = zp
self.state = state
self.city = city
def add_phone(self, number):
self.phone = number
# Observe that these functions are not belonging to the class.
def _print_layout(object):
print object.fname, object.lname
print '==========================='
print 'ADDRESS:'
print object.address
print object.zip
print object.state
print object.city
print '\nPHONE:'
print object.phone
print '\n'
def print_customer(customer_name):
_print_layout(customers[customer_name])
def print_customers():
for customer_name in customers.iterkeys():
_print_layout(customers[customer_name])
if __name__ == '__main__':
# Add some customers to dictionary:
customers['Steve'] = Customer('Steve', 'Jobs')
customers['Niclas'] = Customer('Niclas', 'Nilsson')
# Add some more data
customers['Niclas'].add_address('Some road', '12312', 'WeDon\'tHaveStates', 'Hultsfred')
customers['Steve'].add_phone('123-543 234')
# Search one customer and print him
print 'Here are one customer searched:'
print 'ooooooooooooooooooooooooooooooo'
print_customer('Niclas')
# Print all the customers nicely
print '\n\nHere are all customers'
print 'oooooooooooooooooooooo'
print_customers()
It is generally not that useful to have dynamically generated variable names. I would definitely suggest something like Niclas' answer instead, but if you know this is what you want here is how you can do it:
for record in result:
objectname = 'Customer' + str(record[0])
print objectname
customername = str(record[1])
exec '%s = Customer(%r)' % (customername, customername)
This will result in the variables Customer1 and Customer2 being added to the innermost scope, exactly like if you had executed the following lines:
Customer1 = Customer('Customer1')
Customer2 = Customer('Customer2')
When doing it this way you need to make sure that customername is a valid Python identifier.
What you need is a dictionary:
customers = {}
for record in result:
objectname = 'Customer' + str(record[0])
customers[customername] = Customer(str(record[1])) #assignment to dictionary

Categories