Converting a String to a Class (and vice versa) in Python - python

For a project that I am working on, I need to convert a string (which has ID numbers (ie ID_00000001...ID_00002032 or something like that)) into a class.
By that I mean, there is already a class stored that has values like so:
class ID_00000001:
name = 'Robert Johnson'
age = 193
facebook_url = 'facebook.com/Robert_johnson'
etc.
I want to make it so that I can compare this profile to others. The way I was thinking of doing this was comparing values like (psudo):
current_profile_str = 'ID_00000001'
for i in range(len(all_IDs)):
matches = []
num = *converts number into 8 digit str*
cycle_profile_str = 'ID_' + num
*convert current_profile and cycle_profile to class*
if (current_profile.age == cycle_profile.age):
matches.append(cycle_profile_str)
So the question that I suppose I have in this context, is how would I be able to convert a string to a class?

You are using classes wrongly. You have one class, a facebook user.
This could look like this:
class FacebookUser():
def __init__(self, id, name, age, url):
self.id = id
self.name = name
self.age = age
self.url = url
You can then create instances for each user.
user1 = FacebookUser(1, 'John Doe', 27, 'facebook.com/john_doe')
user2 = FacebookUser(3, 'Jane Doe', 92, 'facebook.com/jane_doe')
print(user1.age == user2.age)
To represent a class as a string, you can add the __repr__ magic function to the class:
class FacebookUser():
def __init__(self, id, name, age, url):
self.id = id
self.name = name
self.age = age
self.url = url
def __repr__(self):
return 'FacebookUser(id: {:08d}, name: {}, age: {})'.format(
self.id,
self.name,
self.age,
)
This will result in
>>> print(user1)
FacebookUser(id: 00000001, name: John Doe, age: 27)
For the other way around, you would implement an alternative constructor using
a class method. These are methods belonging to a class, not to the instance.
The first argument in these functions is the class cls, not the instance self:
class FacebookUser():
def __init__(self, id, name, age, url):
self.id = id
self.name = name
self.age = age
self.url = url
def __repr__(self):
return 'FacebookUser(id: {:08d}, name: {}, age: {})'.format(
self.id,
self.name,
self.age,
)
#classmethod
def from_string(cls, string):
'''
Create a new instance from a string with format
"id,name,age,url"
'''
_id, name, age, url = string.split(',')
return cls(int(_id), name, int(age), url)
user1 = FacebookUser.from_string('1,John Doe,27,facebook.com/john_doe')

MaxNoe's answer is probably what you're looking for, but anyway...
If you really, actually want to somehow build up a class from a sequence of strings (For example, to have variables with their names extracted from the string itself, then you might want to look at metaclasses.
http://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/

Related

Outputting Specific Prosperities of an Array(List) of Objects in Python

I have an class Student which has an array(list) of Objects called Students. I am trying to output the names of all the students in the array.
class Student(object):
name = ""
age = 0
major = ""
# The class "constructor" - It's actually an initializer
def __init__(self, name, age, major):
self.name = name
self.age = age
self.major = major
Students = []
Students.append(Student("Dave",23,"Chem"))
Students.append(Student("Emma",34,"Maths"))
Students.append(Student("Alex",19,"Art"))
print(Students[0].__dict__)
print(Students[1].__dict__)
print (Students[0])
Both the ways I have found and tired do not output the specific name but the location or the whole object. Is there a way to just output the name? For example output Students[0] name Dave
{'name': 'Emma', 'age': 34, 'major': 'Maths'}
<__main__.Student object at 0x000001FCDE4C2FD0>```
If you just want the name, you can print(Students[0].name). If you want to see the relevant attributes when printing a Student object, you can implement the __repr__ method.
class Student:
# The class "constructor" - It's actually an initializer
def __init__(self, name, age, major):
self.name = name
self.age = age
self.major = major
def __repr__(self):
return f"<Student name={self.name} age={self.age} major={self.major}>"
This way you can simply do print(Students[0]) to see the name, age and major of a student.
By the way, for a normal class definition, you want to initialize instance attributes inside the __init__ method, instead of declaring them above __init__: those are class attributes. Please read this section of the documentation to familiarize yourself with the syntax.

when to use getter and setter with property?

When should you use a property with getters/setters? It is not pythonic or wrong to not use a property with getters and setters? Should or shouldn't I write it with a property?
Examples:
class Person:
def __init__(self, firstname, lastname, age):
self.firstname = firstname
self.lastname = lastname
self.age = age
def say_hi(self):
print(f"""Hi i'm {self.firstname} {self.lastname} and i'm {self.age}""")
#property
def age(self):
return self._age
#age.setter
def age(self, newage):
if not isinstance(newage, int):
raise TypeError("Expect an Integer")
self._age = newage
versus
class Person2:
def __init__(self, firstname, lastname, age):
self.firstname = firstname
self.lastname = lastname
self.age = age
def say_hi(self):
print(f"""Hi i'm {self.firstname} {self.lastname} and i'm {self.age}""")
def get_age(self):
return self.age
def set_age(self, newage):
if not isinstance(newage, int):
raise TypeError("Expect an Integer")
self.age = newage
You should generally prefer to use "protected" variables (such as those starting with _) with properties (not separate functions that users need to call, that's just clunky), as it confers some advantages. This encapsulation is very handy as it:
lets you control the internal data completely, such as preventing people entering ages like -42 (which they will do if they can); and
lets you change the underlying implementation in any manner you want, without affecting clients.
For example on that last point, you may want to maintain a separate structure of all names and simply store references to those names in your Person class. This can allow you to store many more names, as the surname "Von Grimmelshausen" would be stored once (in the separate structure) and as much smaller indexes in all the Person objects that use it.
You can then totally change the naive getter from:
#property
def surname(self):
return self._surname
to:
#property
def surname(self):
return self._surname_db[self._surname_index]
without any changes to clients.
The pythonic way would be not to use setters and getters at all; just have an attribute:
class Person:
def __init__(self, firstname, lastname, age):
self.firstname = firstname
self.lastname = lastname
self.age = age
def say_hi(self):
print(f"Hi i'm {self.firstname} {self.lastname} and i'm {self.age}")
If you want to check types, use type annotations and a checker like mypy:
class Person:
def __init__(self, firstname, lastname, age):
self.firstname: str = firstname
self.lastname: str = lastname
self.age: int = age
def say_hi(self):
print(f"Hi i'm {self.firstname} {self.lastname} and i'm {self.age}")
If it later turns out that you do need to do something more complex, you can always turn it into a property later with no change of interface.
"Pythonic" is a holy struggle.
I personally prefer the Class under full control.
In your case:
class Person:
def __init__(self, firstname, lastname, age):
self.firstname = firstname
self.lastname = lastname
self.age = age
def say_hi(self):
print(f"Hi i'm {self.firstname} {self.lastname} and i'm {self.age}")
def test_str(self, cosi):
return self.test(cosi, str)
#staticmethod
def test(cosi, neco):
assert isinstance(cosi, neco), f"Bad value! {cosi} is not instance" \
f" from {neco.__name__}"
return cosi
#staticmethod
def test_positiv_int(num):
assert 0 < int(num), f"Expect an positiv integer" # negative value protect
return int(num) # if int is like string this returned int
def __setattr__(self, key, value):
# important!!!:
whitedict = dict(firstname=self.test_str,
lastname=self.test_str,
age=self.test_positiv_int
)
# Call fn from whitedict with parameter
self.__dict__[key] = whitedict[key](value)
The second version of your code (referring to class2) utilizes two instance methods i.e get_age and
set_age which are not serving much of a purpose because you can retrieve the age attribute of an instance without calling the get_age method, also you can set the age attribute to literally anything without invoking your set_age method. Also if you want user to retrieve or set the age attribute using your given instance methods, the user who was using your class previously will have to make changes in their code which is something we do not want.
Now, the first version of your code (referring to class1) is very helpful because you can pose restrictions on the age attribute by using property decorators. You can make the age attribute read only or both read and write and you can just retrieve or set the age attribute of an instance normally without having to call any methods.
Also, as you explicitly need to call the set_age method on an instance in second version of your code, for this
piece of logic :
if not isinstance(newage, int):
raise TypeError("Expect an Integer")
self._age = newage
to execute so the user cannot put any arbitrary value into the age attribute, on the other hand it happens implicitly whenever you try to set the age attribute when you use properties.

How to search for a name in a list and remove it with a method in oop - python

I wrote a program with which you can manage the employees of a company. I save all my objects that I create in my list self .__ staffs = []. Yes, I know that this is a private attribute and so do everyone else in my program, but that was the default. If I now create my instances of the classes and save them in the list self .__ staffs = [] using the add_staff method, how can I define a method where I can find the name of the worker and whether he is a Worker, Secretary or Manager and delete it from my list? I've already tried it with .remove(name), but here it only removes the instance of the Staff class, without the data on whether he is a worker, .. or not. You should look for the staff object from the self .__ staffs attribute by name and then remove it.Can anyone help me? Thanks in advance
Here is my Code:
class StaffList:
def __init__(self, date: str):
self.__date = date
self.__staffs = []
def get_count(self):
t = len(self.__staffs)
for staff in self.__staffs:
t -= 0.5
return int(t)
def add_staff(self, staff):
self.__staffs.append(staff)
def remove_by_name(self, name):
self.__staffs.remove(name)
def __str__(self):
a = "Date: {}\nThere are {} Staff-Member(s):\n".format(self.__date, self.get_count())
for i in self.__staffs:
a += i.__str__()
return a
class Staff:
def __init__(self, name: str, salary: int):
self.__name = name
self.__salary = salary
def __str__(self):
return " -Members Name and Salary: {} [{}€]\n".format(self.__name, self.__salary)
class Manager(Staff):
def __init__(self, name, salary, department: str):
super().__init__(name, salary)
self.__department = department
def __str__(self):
return " This Member is a Manager and works in the {} department\n".format(self.__department)
class Secretary(Staff):
def __init__(self, name, salary, has_printer: bool):
super().__init__(name, salary)
self.__has_printer = has_printer
def __str__(self):
if self.__has_printer is True:
return " This Member is a Secretary and has a Printer\n"
else:
return " This Member is a Secretary and has no Printer\n"
class Worker(Staff):
def __init__(self, name, salary, has_driving_licence: bool):
super().__init__(name, salary)
self.__has_driving_licence = has_driving_licence
def __str__(self):
if self.__has_driving_licence is True:
return " This Member is a Worker and has a driving licence\n"
else:
return " This Member is a Wroker and has no driving licence\n"
datum = StaffList("22.11.2020")
staff1 = Staff("Josep Lanington", 2500)
datum.add_staff(staff1)
manager1 = Manager(staff1, staff1, "Elektronics")
datum.add_staff(manager1)
staff2 = Staff("Elena Kromberger", 1800)
datum.add_staff(staff2)
secretary1 = Secretary(staff2, staff2, True)
datum.add_staff(secretary1)
staff3 = Staff("Peter Angerer", 1500)
datum.add_staff(staff3)
worker1 = Worker(staff3, staff3, False)
datum.add_staff(worker1)
print(datum)
First of all, your get_count method is wrong. Should be
def get_count(self):
return len(self.__staffs)
you are doing something really strange here:
manager1 = Manager(staff1, staff1, "Elektronics")
datum.add_staff(manager1)
You created an instance of class Manager, giving another instance of class as name and salary.
I guess what you are trying to achieve is this:
staff1 = Manager("Josep Lanington", 2500, "Elektronics")
datum.add_staff(staff1)
staff2 = Secretary("Elena Kromberger", 1800, True)
datum.add_staff(staff2)
staff3 = Worker("Peter Angerer", 1500, False)
datum.add_staff(staff3)
print(datum)
Now, in order to remove an item from a list with .remove() method you should be able to compare this item to .remove() parameter.
But your class don't know how to compare parameter with a string.
You could add this method to your Staff() class.
def __eq__(self, name):
if not isinstance(name, str):
# don't attempt to compare against unrelated types
return NotImplemented
return self.__name == name
Now you can do datum.remove_by_name("Elena Kromberger").
This is not very elegant (since you define method __eq__ to work against string) but this might give you an idea how to move forward.
Another thing to consider is that if there is no Staff member with such name you would get an error
ValueError: list.remove(x): x not in list

Weird output in Python

I am generating a list of employees and managers. However, I obtained this weird output after trying sort them by last name (my initial class employee only contains "name" but not divide into first name and last name. Then, I use [1] to indicate the last name). What's wrong with my code since I can't see my list of employees.
class Employee:
def __init__(self, name, socialSecurityNumber, salary):
"""
Set name, socialSecurityNumber, and salary to itself.
"""
self.name = name
self.socialSecurityNumber = socialSecurityNumber
self.salary = salary
employeeList = []
employee1 = Employee("Banny Chu", "777-88-9999", 45000)
employee2 = Employee("Luffy Monkey", "555-66-9999", 32000)
employee3 = Employee("Zoro Nonoroa", "222-00-3333", 37000)
manager1 = Manager("Scalt Haight", "444-33-1111", 60000, "Lab", 2300)
manager2 = Manager("Kapu Ro", "333-44-2222", 65000, "General", 2600)
manager3 = Manager("Nami Swan", "111-77-6666", 80000, "HR", 3000)
employeeList.append(employee1)
employeeList.append(employee2)
employeeList.append(employee3)
employeeList.append(manager1)
employeeList.append(manager2)
employeeList.append(manager3)
print (sorted(employeeList, key=lambda employee: employee.name[1].lower()))
Output as below (strange output since I can't see my employeeList in the correct format even though I type print(employeeList) and gave the same format as below.
[<employee8.Employee object at 0x105a48b00>, <manager8.Manager object at 0x1054290f0>, <manager8.Manager object at 0x1054290f0>, <manager8.Manager object at 0x1054290f0>, <manager8.Manager object at 0x1054290f0>]
What should I modify it so that I can see my sorted list in the way that I can clearly see them?
By default, user-defined objects will be represented as a class instance at a location in memory:
<__main__.Employee instance at 0x02A39940>
You will need to add a special method for object representation in your Employee class:
class Employee:
def __init__(self, name, socialSecurityNumber, salary):
"""
Set name, socialSecurityNumber, and salary to itself.
"""
self.name = name
self.socialSecurityNumber = socialSecurityNumber
self.salary = salary
def __repr__(self):
return self.name # represents object with name
You're missing the point that sorted returns a permutation of the list that got sorted based on the criteria you sent it. It doesn't automagically return just the keys you sorted them on if that's what you were expecting?
sort = sorted(employeeList, key=lambda employee: employee.name[1].lower())
print([emp.name.split()[1] for emp in employeeList])
Output (I was lazy and only copy pasted 3 of your employees):
['Chu', 'Monkey', 'Nonoroa']
You were also missing a split, because you save your name in a single string. Indexing a single string will return a single character at that location in a string.
If your goal wasn't to print out just the last names, then you have to override either the __str__ or __repr__ method. (Read about what exact difference is between the methods here.)
You forgot to split the name in the key function: employee.name.split(' ')[1].
Python calls __repr__ on the sorted list which prints '[' and ']' at the beginning and end and then calls __repr__ on each list element. The default __repr__ prints the object type and address. If you want to see something else, you have to give python another __repr__ to call.
class Employee:
def __init__(self, name, socialSecurityNumber, salary):
"""
Set name, socialSecurityNumber, and salary to itself.
"""
self.name = name
self.socialSecurityNumber = socialSecurityNumber
self.salary = salary
def __repr__(self):
return "{} {} {}".format(self.name, self.socialSecurityNumber, self.salary)
class Manager(Employee):
def __init__(self, name, socialSecurityNumber, salary, department, unknown):
super().__init__(name, socialSecurityNumber, salary)
employeeList = []
employee1 = Employee("Banny Chu", "777-88-9999", 45000)
employee2 = Employee("Luffy Monkey", "555-66-9999", 32000)
employee3 = Employee("Zoro Nonoroa", "222-00-3333", 37000)
manager1 = Manager("Scalt Haight", "444-33-1111", 60000, "Lab", 2300)
manager2 = Manager("Kapu Ro", "333-44-2222", 65000, "General", 2600)
manager3 = Manager("Nami Swan", "111-77-6666", 80000, "HR", 3000)
employeeList.append(employee1)
employeeList.append(employee2)
employeeList.append(employee3)
employeeList.append(manager1)
employeeList.append(manager2)
employeeList.append(manager3)
for employee in sorted(employeeList, key=lambda employee: employee.name.split(' ')[1].lower()):
print(employee)

Python, creating objects

I'm trying to learn python and I now I am trying to get the hang of classes and how to manipulate them with instances.
I can't seem to understand this practice problem:
Create and return a student object whose name, age, and major are
the same as those given as input
def make_student(name, age, major)
I just don't get what it means by object, do they mean I should create an array inside the function that holds these values? or create a class and let this function be inside it, and assign instances? (before this question i was asked to set up a student class with name, age, and major inside)
class Student:
name = "Unknown name"
age = 0
major = "Unknown major"
class Student(object):
name = ""
age = 0
major = ""
# The class "constructor" - It's actually an initializer
def __init__(self, name, age, major):
self.name = name
self.age = age
self.major = major
def make_student(name, age, major):
student = Student(name, age, major)
return student
Note that even though one of the principles in Python's philosophy is "there should be one—and preferably only one—obvious way to do it", there are still multiple ways to do this. You can also use the two following snippets of code to take advantage of Python's dynamic capabilities:
class Student(object):
name = ""
age = 0
major = ""
def make_student(name, age, major):
student = Student()
student.name = name
student.age = age
student.major = major
# Note: I didn't need to create a variable in the class definition before doing this.
student.gpa = float(4.0)
return student
I prefer the former, but there are instances where the latter can be useful – one being when working with document databases like MongoDB.
Create a class and give it an __init__ method:
class Student:
def __init__(self, name, age, major):
self.name = name
self.age = age
self.major = major
def is_old(self):
return self.age > 100
Now, you can initialize an instance of the Student class:
>>> s = Student('John', 88, None)
>>> s.name
'John'
>>> s.age
88
Although I'm not sure why you need a make_student student function if it does the same thing as Student.__init__.
Objects are instances of classes. Classes are just the blueprints for objects. So given your class definition -
# Note the added (object) - this is the preferred way of creating new classes
class Student(object):
name = "Unknown name"
age = 0
major = "Unknown major"
You can create a make_student function by explicitly assigning the attributes to a new instance of Student -
def make_student(name, age, major):
student = Student()
student.name = name
student.age = age
student.major = major
return student
But it probably makes more sense to do this in a constructor (__init__) -
class Student(object):
def __init__(self, name="Unknown name", age=0, major="Unknown major"):
self.name = name
self.age = age
self.major = major
The constructor is called when you use Student(). It will take the arguments defined in the __init__ method. The constructor signature would now essentially be Student(name, age, major).
If you use that, then a make_student function is trivial (and superfluous) -
def make_student(name, age, major):
return Student(name, age, major)
For fun, here is an example of how to create a make_student function without defining a class. Please do not try this at home.
def make_student(name, age, major):
return type('Student', (object,),
{'name': name, 'age': age, 'major': major})()
when you create an object using predefine class, at first you want to create a variable for storing that object. Then you can create object and store variable that you created.
class Student:
def __init__(self):
# creating an object....
student1=Student()
Actually this init method is the constructor of class.you can initialize that method using some attributes.. In that point , when you creating an object , you will have to pass some values for particular attributes..
class Student:
def __init__(self,name,age):
self.name=value
self.age=value
# creating an object.......
student2=Student("smith",25)

Categories