Program to use class method in python - python-3.x

class student:
college = "tcet"
def __init__(self,name,age,marks):
self.name = name
self.age = age
self.marks = 90
#staticmethod #This is a StaticMethod Decorator
def info():
return "This is data of students"
#classmethod #This is a ClassMethod Decorator
def college(cls):
return cls.college
s1 = student("shubham",19,100)
s2 = student("luffy",20,99.99)
print(student.college())
print(student.info())
Getting this error:
<bound method student.college of <class '__main__.student'>>

Methods are just another form of attributes in Python, so by naming a method college when a class attribute college has already been defined, the method object will replace the class attribute college that was holding the string "tcet".
You can correct this by simply naming the attribute differently:
class student:
_college = "tcet"
#classmethod
def college(cls):
return cls._college

Related

How to call a function within a class in Python3

When running this code, I am attempting to get the same output in flipper.swim() that I get from Salmon.swim(). The Salmon.swim() properly outputs the string from that method, but when I call flipper.swim(), I get the following error: TypeError: swim() takes 0 positional arguments but 1 was given
class Fish (object):
def __init__(self, name):
self.name = name
def swim():
print("The fish swam.")
class Salmon(Fish):
pass
flipper = Fish("FLIPPER")
Salmon.swim()
Fish.swim()
print(flipper.name)
flipper.swim()
I think this is what you are trying to do
class Fish (object):
def __init__(self, name):
self.name = name
def swim(self):
print(self.name + " swam.")
class Salmon(Fish):
pass
flipper = Fish("FLIPPER")
salmon = Salmon("FOO")
Salmon.swim(flipper)
Fish.swim(flipper)
print(flipper.name)
Fish.swim(flipper)
Fish.swim(salmon)
The above will output:
FLIPPER swam.
FLIPPER swam.
FLIPPER
FLIPPER swam.
FOO swam.
you get the error because python automatically pass the instance of your class (flipper in your sample code) as the first argument to your method swim, that by convention we call self, to turn off this behavior you need to specify that said method doesn't require/use your instance of this class, and that is done with the staticmethod decorator
class Fish (object):
def __init__(self, name):
self.name = name
#staticmethod
def swim():
print("The fish swam.")

Multiple inheritance, super() and their correct use with arguments in Python

I'm trying to understand multiple inheritance in python. I think that "kinda" got it, but I'm missing a few pieces. I know that if I have two clases I can do something like:
class A():
def __init__(self,name):
self.name = name
class B(A):
def __init__(self,name):
A.__init__(self,name)
self.mm = False
self.name = name
b = B("Peter")
My problem is when I have more classes and each class has their own init arguments. At first glance, it makes like no sense to have something like this:
class A():
def __init__(self,name,arg_a1,arg_a2):
self.name = name
class B(A):
def __init__(self,name,arg_b1,arg_b2,arg_a1,arg_a2...):
A.__init__(self,name,arg_a1,arg_a2...)
self.mm = False
self.name = name
class C(B):
def __init__(self,name,arg_c1,arg_c2,arg_b1,arg_b2,arg_a1,arg_a2.........):
B.__init__(self,name,arg_b1,arg_b2,arg_a1,arg_a2...)
self.name = name
So I started to look how to do it in an efficient way and not just hardcode it. Thats when I came across with multiple inheritance and thats when my doubts started to arraise.
If I have 3 classes:
class A():
def __init__(self,name):
self.name = name
class B(A):
def __init__(self,name,*args,**kwargs):
super().__init__(*args,**kwargs)
self.mm = False
self.name = name
class C(B):
def __init__(self,a,j,*args,**kwargs):
super().__init__(*args,**kwargs)
self.a = a
self.j = j
c = C("p",1,5,name="p")
Why this give an error but adding name as an init argument does not?
In this other example, if I add another argument to A init's function the I get TypeError: __init__() got multiple values for argument 'name'.
class A():
def __init__(self,name,lastname):
self.name = name
self.lastname = lastname
class B(A):
def __init__(self,name,*args,**kwargs):
super().__init__(name,*args,**kwargs)
self.mm = False
self.name = name
class C(B):
def __init__(self,a,j,*args,**kwargs):
super().__init__(*args,**kwargs)
self.a = a
self.j = j
c = C("p",1,5,name="p")
So, after all this, several questions comes to my mind.
Why this TypeError is generated?
How can I make inheritance "smart"?
Do I always need to use *args and **kwargs with multiple inheritance?
And all this gets me to the point to the libraries I use daily. Probably some of them use this concetps (I don't know, I'm assuming). What happes when the user puts a kwarg that is not present in any class? How do python "knows" that name goes in class A and not class B or viceversa?

Decorators unexpectedly change constructor behavior in Python

Below, I show a simplified example of a more complicated code, but nonetheless, it fully represents the issue that I have encountered.
Part 1: this works fine, no issues:
class Animal():
def __init__(self, animal_name = "no name given"):
self.set_name(animal_name)
def get_name(self):
return self._animal_name
def set_name(self, animal_name):
self._animal_name = animal_name
class Dog(Animal):
def __init__(self, dog_breed = "no breed", dog_name = "no name given"):
self._dog_breed = dog_breed
super().__init__(dog_name)
def get_breed(self):
print(self._dog_breed)
x = Dog('Greyhound', 'Rich')
Part 2: after introducing getter & setter decorators, the code stops working:
class Animal():
def __init__(self, animal_name = "no name given"):
#THE LINE BELOW SEEMS TO CAUSE AN ISSUE
self.name(animal_name)
#property
def name(self):
return self._animal_name
#name.setter
def name(self, animal_name):
self._animal_name = animal_name
class Dog(Animal):
def __init__(self, dog_breed = "no breed", dog_name = "no name given"):
self._dog_breed = dog_breed
super().__init__(dog_name)
def get_breed(self):
print(self._dog_breed)
x = Dog('Greyhound', 'Rich')
Output: AttributeError: 'Dog' object has no attribute '_animal_name'
When I keep the decorators in Part 2 but change the constructor in the Animal class to:
class Animal():
def __init__(self, animal_name = "no name given"):
self._animal_name=animal_name
It works.
I am just curious why it doesn't work in the example above in Part 2?
Short answer:
The line
self.name(animal_name)
can be split in two parts:
tmp = self.name
tmp(animal_name)
First, self.name calls the getter and the result is treated as a function. The getter uses return self._animal_name and since the setter has never been called, the respective error occurs.
Long answer:
Let's take the following class:
class Animal:
def __init__(self, animal_name):
self.name(animal_name)
#property
def name(self):
return self._animal_name
#name.setter
def name(self, animal_name):
self._animal_name = animal_name
To understand what the line
self.name(animal_name)
actually does, you first need to understand decorators.
The code
#dec
def func(a, b, ...):
[...]
is equivalent to
def func_impl(a, b, ...):
[...]
func = dec(func_impl)
(except that you can not call func_impl directly). See, for example, PEP 318 for more information.
This means that you can write the Animal class from above without using decorators:
class Animal:
def __init__(self, animal_name):
self.name(animal_name)
def get_name(self):
return self._animal_name
name = property(get_name)
def set_name(self, animal_name):
self._animal_name = animal_name
name = name.setter(set_name)
In order to understand this code, you need to understand the builtin property, which is a class. See the python docs for detailed information.
The line name = property(get_name) creates an object of type property. When retrieving the value of the property, get_name is called.
The line name = name.setter(set_name) first calls name.setter(set_name), which creates a copy of the property, and then overwrites name with this copy. When assigning a value to the copy, set_name is called.
All in all, name is an object of type property that uses get_name as getter and set_name as setter.
How does this help?
You need to understand this: name is not a function. It is a property. It is not callable.
The problematic line
self.name(animal_name)
is actually equivalent to
self.get_name()(animal_name)
which this explains the error message: The constructor calls the getter, which tries to use return self._animal_name. But since the setter has not been called, yet, self._animal_name has not been set.

How to share the parent class variables to third class through the child class in python?

I have the following code:
class Parent():
def __init__(self):
self.variable1 = self.method1()
self.variable2 = self.method2()
self.variable3 = self.method3()
self.variable4 = self.method4()
#.........(for example I have 100 variables here)
def method1(self):
return 100
def method2(self):
return 200
def method3(self):
return 300
def method4(self):
return 400
class Third():
def __init__(self):
a = 1
def call(self):
value = self.variable3 + 1
return value
class Child(Parent):
def __init__(self):
super().__init__()
self.getanswer = self.method11()
def method11(self):
value_count = Third().call()
return value_count
obj = Child()
It throwed the following Error:
AttributeError: 'Third' object has no attribute 'variable3'
Here I wanted to send all the values of parent to Third Class through Child class. How can I achieve it? I know that I can pass Parent class variables seperately in directly as a parameter in the class Third as following:
value_count = Third(self.variable3).call()
and change the Third Class constructor accordingly. But I don't want to do it as my Parent class has some time taking operations to do.
Also I don't want to make class Third as child to the class Parent.
So How can I recognize the Parent class variables(variable1, variable2, variable3, variable4, ....) in Third Class through Child Class ?

How to extend base class to child class in python and printing child class value?

when i try to extend base class to child class it doesn't work properly
it shows error
b1 = B("adnan", 25, "male")
TypeError: object() takes no parameters
here is my code :
class A:
def letter(self,name,age):
self.name=name
self.age=age
class B(A):
def farhan(self,gender):
self.gender=gender
b1=B("adnan",25,"male")
print(b1.name,b1.age,b1.gender)
None of your classes have an __init__ method, which is used to initialise the class. When you do: B("adnan",25,"male"), it's translated to a call to B.__init__:
B.__init__(<object of type B>, "adnan", 25, "male")
The default implementation of __init__ supplied by the class object takes no parameters, which is exactly what the error is saying. A inherits from object (issubclass(B, object) == True), so its default __init__ method is the same as that of object.
You can try this:
class A:
def __init__(self, name, age):
self.name=name
self.age=age
class B(A):
def __init__(self, name, age, gender):
super().__init__(name, age) # initialise parent
self.gender = gender
When you write something like
b1 = B("adnan", 25, "male")
You are creating a new instance of the class 'B'. When you do that, you're calling the __init__ method of that class. A possible solution would be in the lines of:
class B(A):
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
You need to brush up your Python OOP skills! A nice source is https://realpython.com/python3-object-oriented-programming/

Resources