Python3 面向对象
类定义、类对象与实例对象、方法、继承、多继承、方法重写与类属性。
面向对象技术简介
Object orientation bundles data with the methods that operate on it, and organises systems through encapsulation, inheritance and polymorphism. In Python everything is an object — numbers, strings, functions, classes themselves.
- Class — a template; Instance — a concrete object built from it
- Encapsulation — hide internals behind a public interface
- Inheritance — reuse and extend a parent class
- Polymorphism — same call, different behaviour per type
类定义
Define a class with class Name:, conventionally PascalCase. __init__ is the constructor, called automatically on instantiation; self is the first parameter standing for the instance and is passed implicitly.
class Employee:
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def display_count(self):
print("Total Employee:", Employee.empCount)
def display_employee(self):
print("Name:", self.name, "Salary:", self.salary)
e1 = Employee("Zara", 2000)
e2 = Employee("Manni", 5000)
e1.display_employee()
e2.display_employee()
e2.display_count()Name: Zara Salary: 2000
Name: Manni Salary: 5000
Total Employee: 2类对象
A class is itself an object supporting two operations: attribute reference Obj.name and instantiation Obj(). Class attributes are created at class definition and shared by all instances; reading through an instance falls back to them.
class Employee:
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
print(Employee.empCount)
e1 = Employee("Zara", 2000)
print(Employee.empCount)
print(e1.empCount)
e1.empCount = 99
print(e1.empCount, Employee.empCount)0
1
1
99 1e1.empCount = 99 does not modify the class attribute — it shadows it with a new instance attribute. To change the shared value you must write Employee.empCount = ....类的方法
Three method kinds: instance methods take self, @classmethod takes the class as cls, and @staticmethod takes nothing implicit. @property exposes a method as a readable attribute.
class Circle:
pi = 3.14159
def __init__(self, r):
self.r = r
def area(self):
return Circle.pi * self.r ** 2
@classmethod
def unit(cls):
return cls(1)
@staticmethod
def describe():
return "a circle helper"
@property
def diameter(self):
return self.r * 2
c = Circle(2)
print(round(c.area(), 2))
print(round(Circle.unit().area(), 2))
print(Circle.describe())
print(c.diameter)12.57
3.14
a circle helper
4继承
Put the parent class in parentheses to inherit. The child gets the parent's attributes and methods and can reuse them via super(). isinstance() returns true for the parent type too.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def intro(self):
return "%s, %d" % (self.name, self.age)
class Student(Person):
def __init__(self, name, age, school):
super().__init__(name, age)
self.school = school
def intro(self):
return super().intro() + " @ " + self.school
s = Student("Tom", 20, "MIT")
print(s.intro())
print(isinstance(s, Student), isinstance(s, Person))
print(issubclass(Student, Person))Tom, 20 @ MIT
True True
True多继承
A class may inherit from several parents. Lookup follows the MRO: depth-first, left to right, each class appearing once. Inspect it with Class.__mro__.
class A:
def who(self):
return "A"
class B(A):
def who(self):
return "B"
class C(A):
def who(self):
return "C"
class D(B, C):
pass
print(D().who())
print([c.__name__ for c in D.__mro__])
print(D.__mro__[1].__name__)B
['D', 'B', 'C', 'A', 'object']
B方法重写
Redefining a parent method in the child overrides it. To keep the parent behaviour as well, call super().method() explicitly — that extends instead of replacing.
class Animal:
def speak(self):
return "..."
class Dog(Animal):
def speak(self):
return "Woof"
class Puppy(Dog):
def speak(self):
return super().speak() + "!"
for a in (Animal(), Dog(), Puppy()):
print(type(a).__name__, a.speak())Animal ...
Dog Woof
Puppy Woof!类属性与方法
Class attributes belong to the class and are shared; instance attributes belong to one object. A double leading underscore triggers name mangling (__x becomes _Class__x) — a convention, not enforcement.
class JustCounter:
__secretCount = 0
publicCount = 0
def count(self):
self.__secretCount += 1
self.publicCount += 1
c = JustCounter()
c.count()
c.count()
print(c.publicCount)
print(c._JustCounter__secretCount)
try:
print(c.__secretCount)
except AttributeError as e:
print("AttributeError:", e)2
2
AttributeError: 'JustCounter' object has no attribute '__secretCount'_name to signal "internal" — that is the Pythonic convention.面向对象技术简介
面向对象把数据和操作数据的方法打包成「类」,用「封装、继承、多态」来组织复杂系统。Python 里一切皆对象 —— 数字、字符串、函数、类本身都是对象。
- 类 —— 模板;实例 —— 由类创建的具体对象
- 封装 —— 用公开接口隐藏内部实现
- 继承 —— 复用并扩展父类
- 多态 —— 同样的调用,不同类型有不同行为
类定义
用 class 类名: 定义类,类名习惯用大驼峰。__init__ 是构造方法,创建实例时自动调用;self 是第一个参数,代表实例本身,调用时不用手动传。
class Employee:
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
def display_count(self):
print("Total Employee:", Employee.empCount)
def display_employee(self):
print("Name:", self.name, "Salary:", self.salary)
e1 = Employee("Zara", 2000)
e2 = Employee("Manni", 5000)
e1.display_employee()
e2.display_employee()
e2.display_count()Name: Zara Salary: 2000
Name: Manni Salary: 5000
Total Employee: 2类对象
类本身也是对象,支持两种操作:属性引用 Obj.name 和实例化 Obj()。类属性在类定义时创建,所有实例共享同一份;通过实例读取会回退到类属性。
class Employee:
empCount = 0
def __init__(self, name, salary):
self.name = name
self.salary = salary
Employee.empCount += 1
print(Employee.empCount)
e1 = Employee("Zara", 2000)
print(Employee.empCount)
print(e1.empCount)
e1.empCount = 99
print(e1.empCount, Employee.empCount)0
1
1
99 1e1.empCount = 99 并不会修改类属性,而是给实例新建了一个同名属性把它「遮住」。要改共享值必须写 Employee.empCount = ...。类的方法
类里有三种方法:实例方法第一个参数是 self,@classmethod 第一个参数是类 cls,@staticmethod 没有隐含参数。@property 则把方法伪装成属性来读。
class Circle:
pi = 3.14159
def __init__(self, r):
self.r = r
def area(self):
return Circle.pi * self.r ** 2
@classmethod
def unit(cls):
return cls(1)
@staticmethod
def describe():
return "a circle helper"
@property
def diameter(self):
return self.r * 2
c = Circle(2)
print(round(c.area(), 2))
print(round(Circle.unit().area(), 2))
print(Circle.describe())
print(c.diameter)12.57
3.14
a circle helper
4继承
在类名后的括号里写父类即可继承。子类自动获得父类的属性和方法,并可以调用 super() 复用父类实现。isinstance() 对子类实例同时返回父类类型。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def intro(self):
return "%s, %d" % (self.name, self.age)
class Student(Person):
def __init__(self, name, age, school):
super().__init__(name, age)
self.school = school
def intro(self):
return super().intro() + " @ " + self.school
s = Student("Tom", 20, "MIT")
print(s.intro())
print(isinstance(s, Student), isinstance(s, Person))
print(issubclass(Student, Person))Tom, 20 @ MIT
True True
True多继承
一个类可以同时继承多个父类,方法查找按 MRO(方法解析顺序)进行 —— 深度优先、从左到右,且每个类只出现一次。用 类.__mro__ 可以直接查看顺序。
class A:
def who(self):
return "A"
class B(A):
def who(self):
return "B"
class C(A):
def who(self):
return "C"
class D(B, C):
pass
print(D().who())
print([c.__name__ for c in D.__mro__])
print(D.__mro__[1].__name__)B
['D', 'B', 'C', 'A', 'object']
B方法重写
子类定义与父类同名的方法即为重写,调用时用子类的版本。想同时保留父类行为就显式调用 super().方法(),这是扩展而不是替换。
class Animal:
def speak(self):
return "..."
class Dog(Animal):
def speak(self):
return "Woof"
class Puppy(Dog):
def speak(self):
return super().speak() + "!"
for a in (Animal(), Dog(), Puppy()):
print(type(a).__name__, a.speak())Animal ...
Dog Woof
Puppy Woof!类属性与方法
类属性属于类、被所有实例共享;实例属性属于单个实例。私有属性用双下划线开头,会触发名字改写(__x 变成 _类名__x),这是约定而非强制。
class JustCounter:
__secretCount = 0
publicCount = 0
def count(self):
self.__secretCount += 1
self.publicCount += 1
c = JustCounter()
c.count()
c.count()
print(c.publicCount)
print(c._JustCounter__secretCount)
try:
print(c.__secretCount)
except AttributeError as e:
print("AttributeError:", e)2
2
AttributeError: 'JustCounter' object has no attribute '__secretCount'_name 更符合 Python 惯例。