Python, creating objects - python

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)

Related

missing 1 required positional argument (emp1)

I have written code to count employees using class but the the code doesn't work.
class employee:
empCount = 0;
def employee(self, name, salary):
self.name = name;
self.salary = salary;
employee.empCount += 1;
def displayCount(self):
print ("\nTotal Employee %d", Employee.empCount);
def displyEmployee(self):
print('Name:',self.name,'Salary:',self.salary);
emp1 = employee('ABS', 2000)
Your code has a number of problems that are preventing it from working as you expect:
You did not declare your constructor properly. When you do employee('ABS', 200), Python looks for a function called __init__ on the class object. You declared your constructor as employee, similarly to how you would do so in C-based languages. This won't work.
You store the employee count as a variable scoped to the class object. You can do this but I wouldn't because it's a misuse of that capability. Instead, you should create a list of employees and get the length of the list.
Instead of declaring display functions, you should overload the __str__ function, which returns a string representing the object.
Class names should be PascalCase (this doesn't keep your code from working but you should definitely address it).
class Employee:
def __init__(self, name, salary):
self.name = name
self.salary = salary
def __str__(self):
return f"Name: {self.name}, Salary: {self.salary}"
employees = []
employees.append(Employee('ABS', 200))
len(employees) # 1

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.

Why the instance attributes in python are defined three times like this?

why is the instance attribute defined three times in python: once in the parenthesis, once in with the self keyword and once in the assignment
class Details():
def __init__(self,name,age):
self.name = name
self.age = age
__init__ is a function/method and like any other functions has "local variables" just like :
def fn(a, b):
print(a, b)
In your example, name, age in parameters and in right hand side of the assignments are just like a and b. You can choose any name you want for these variables.
But the name and age which are parts of self.name and self.age statements respectively, are attributes of your new-created instance(which self points to it). The name you choose for this attributes will exist in instance's __dict__(instance's namespace dictionary), so that's how you can call them later.
class Details:
def __init__(self, name, age):
self.name = name
self.age = age
obj = Details('John', 20)
print(obj.name)
print(obj.age)
__init__ is the constructor (in OOP terms) to create Details object. in Python, a constructor's first parameter is always self.
self points to the newly created object. It got two attributes, name and age.
the parameters, name and age inside parentheses, can have different names as well. So, the following code works the same :
class Details():
def __init__(self,n,a):
self.name = n
self.age = a

Iterate through instantiated objects of a class

Isit possible to loop through the objects of class person and extract the attributes?
so for e.g. from below code after looping the output would be for each object as such
Object mike, name-Mike, age-20
class Person(object):
def__init__(self,name,age):
self.name = name
self.age = age
mike = Person('Mike',20)
john = Person('John',20)
jack = Person('Jack',20)
adam = Person('Adam',20)
Thanks
Python classes don't automatically track all their instances. You can use a class attribute to track them yourself if you need to:
class Person(object):
people = []
def __init__(self, name, age):
self.name = name
self.age = age
self.people.append(self)
Even better would be to have a separate object that tracked people. The Person.people attribute here is a single global value for the process, which can limit the usability and testability of the class.
To loop the attributes of a given instance, I think you're looking for the builtin function vars:
>>> mike = Person('Mike', 20)
>>> vars(mike)
{'age': 20, 'name': 'Mike'}
To loop through all instances of a given class is not possible, unless you add some code to maintain the list of instances yourself.
if my understanding is right, you are asking about looping the class person
then it can be done like this.
class person(object):
def __init__(self,name,age):
self.name=name
self.age=age
for name,age in {'mike':20,'john':20,'jack':20}.iteritems():
p = person(name,age)
print ('I am %s my age is %d' %(p.name,p.age))
## answer
I am mike my age is 20
I am john my age is 20
I am jack my age is 20

Categories