Since Python is a dynamic language, in general, a dynamic language allows us to:
Bind new properties or methods to objects
You can also unbind the bound properties and methods
If we need to limit the object of a custom type to only bind certain attributes, we can limit it by defining the __slots__ variable in the class.
Note: The limitation of __slots__ is only effective for objects of the current class, and has no effect on subclasses.
class Student(object):
# 用tuple定义允许绑定的属性名称
# 限定Student对象只能绑定 name和age属性
__slots__ = ('name', 'age')
class GraduateStudent(Student):
pass
s = Student() # 创建新的实例
s.name = 'Jackson' # 绑定属性'name'
s.age = 20 # 绑定属性'age'
# s.score = 100
# ERROR: AttributeError: 'Student' object has no attribute 'score'
# slots的限定只对当前类的对象生效,对子类并不起任何作用:
g = GraduateStudent()
g.score = 100
print('g.score =', g.score) # g.score = 100
2. Static methods and class methods
Before, the methods we defined in the class are all object methods, which means that these methods are all messages sent to the object.
In fact, the methods we write in the class do not need to be all object methods, for example:
We define a "triangle" class, construct a triangle by passing in three side lengths, and provide a method to calculate the perimeter and area
But the incoming three side lengths may not be able to construct a triangle object, so we can write a method to verify whether the three side lengths can form a triangle. This method is obviously not an object method, because the triangle object has not been created when this method is called. Come out (because I don’t know if the three sides can form a triangle)
So this method belongs to the triangle class, not the triangle object
We can use static methods to solve this type of problem:
from math import sqrt
class Triangle(object):
def __init__(self, a, b, c):
self._a = a
self._b = b
self._c = c
# 添加静态方法
@staticmethod
def is_valid(a, b, c):
return a + b > c and b + c > a and a + c > b
def perimeter(self):
return self._a + self._b + self._c
def area(self):
half = self.perimeter() / 2
return sqrt(half * (half - self._a) *
(half - self._b) * (half - self._c))
def main():
a, b, c = 3, 4, 5
# 静态方法和类方法都是通过给类发消息来调用的
if Triangle.is_valid(a, b, c):
t = Triangle(a, b, c)
print(t.perimeter())
# 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数
# print(Triangle.perimeter(t))
print(t.area())
# print(Triangle.area(t))
else:
print('无法构成三角形.')
if __name__ == '__main__':
main()
# 12
# 6.0
Similar to static methods, Python can also define class methods in classes:
The first parameter of the class method is named cls by convention
It represents the object of the information related to the current class (the class itself is also an object, and in some places it is also called the metadata object of the class)
Through this parameter, we can obtain information related to the class and create objects of the class
from time import time, localtime, sleep
class Clock(object):
"""数字时钟"""
def __init__(self, hour=0, minute=0, second=0):
self._hour = hour
self._minute = minute
self._second = second
# 定义类方法:
@classmethod
def now(cls):
ctime = localtime(time())
return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)
def run(self):
"""走字"""
self._second += 1
if self._second == 60:
self._second = 0
self._minute += 1
if self._minute == 60:
self._minute = 0
self._hour += 1
if self._hour == 24:
self._hour = 0
def show(self):
"""显示时间"""
return '%02d:%02d:%02d' % \
(self._hour, self._minute, self._second)
def main():
# 通过类方法创建对象并获取系统时间
clock = Clock.now()
while True:
print(clock.show())
sleep(1)
clock.run()
if __name__ == '__main__':
main()
3. Relationship between classes
Simply put, there are three types of relationships between classes: is-a, has-a, and use-a relationships:
The sa relationship (inheritance or generalization), such as the relationship between students and people, and the relationship between mobile phones and electronic products, are all inherited relationships.
The has-a relationship (association), such as the relationship between a department and an employee, and the relationship between a car and an engine, is an association relationship.
Aggregation relationship: if the association relationship is a whole and part association;
Synthetic relationship: If the whole is further responsible for the part of the life cycle (the whole and the part are inseparable, and the same will die at the same time), then this is the strongest relationship.
Use-a relationship (dependency), for example, the driver has a driving behavior (method), in which (parameter) uses the car, then the relationship between the driver and the car is a dependency relationship.
We can use UML (Unified Modeling Language) for object-oriented modeling. One of the important tasks is to describe the relationship between classes and classes with standardized graphic symbols.
4. Inheritance and Polymorphism
As we mentioned earlier, you can create new classes on the basis of existing classes. One way of doing this is to let one class directly inherit properties and methods from another class, thereby reducing the need for repetitive code writing.
The one that provides inheritance information is called the parent class, also called the super class or base class;
The ones that get the inheritance information are called subclasses, also called derived classes or derived classes.
In addition to inheriting the properties and methods provided by the parent class, the subclass can also define its own unique properties and methods, so the subclass has more capabilities than the parent class
Richter's substitution principle: In actual development, we often use subclass objects to replace a parent class object, which is a common behavior in object-oriented programming
The content of the Richter substitution principle can be described as: "A derived class (subclass) object can replace its base class (superclass) (parent class) object in the program."
After the subclass inherits the methods of the parent class, it can give a new implementation version to the existing methods of the parent class. This action is called method override.
Through method rewriting, we can make the same behavior of the parent class have different implementation versions in the subclass. When we call this method overridden by the subclass, different subclass objects will show different behaviors. This is Polymorphism (poly-morphism).
from abc import ABCMeta, abstractmethod
class Pet(object, metaclass=ABCMeta):
"""宠物"""
def __init__(self, nickname):
self._nickname = nickname
# 抽象方法
@abstractmethod
def make_voice(self):
"""发出声音"""
pass
class Dog(Pet):
"""狗"""
def make_voice(self):
print('%s: 汪汪汪...' % self._nickname)
class Cat(Pet):
"""猫"""
def make_voice(self):
print('%s: 喵...喵...' % self._nickname)
def main():
pets = [Dog('旺财'), Cat('凯蒂'), Dog('阿黄')]
for pet in pets:
pet.make_voice()
if __name__ == '__main__':
main()
# 旺财: 汪汪汪...
# 凯蒂: 喵...喵...
# 阿黄: 汪汪汪...
In the above code, we processed the Pet class into an abstract class:
The so-called abstract class is a class that cannot create objects. This kind of class exists to allow other classes to inherit it.
From the grammatical level, Python does not provide support for abstract classes like Java or C#, but we can achieve the effect of abstract classes through the ABCMeta metaclass and abstractmethod wrapper of the abc module.
If there are abstract methods in a class, then the class cannot be instantiated (object creation)
In the above code, Dog and Cat two subclasses respectively rewrite the make_voice abstract method in the Pet class and give different implementation versions.
When we call this method in the main function, this method shows polymorphic behavior (the same method does different things)