一、属性
1、初始化(__init__属性)
作用
对新创建的对象添加属性等必须的资源
说明
a、初始化方法名必须为__init__ 不可改变
b、初始化方法会在构造函数创建实例后自动调用,且将实例自身通过第一个参数self 传入 __init__ 方法
c、构造函数的实参将通过 __init__方法的参数列表,传入到 __init__ 方法中
d、初始化方法内如果需要return 语句返回,则只能返回None
语法
class 类名(继承列表):
def __init__(self[, 形参列表])
语句块
注: [] 里的内容代表可省略
示例
2、析构方法(__del__属性)
作用通常用来释放此对象占用的资源
说明
a、析构方法会在对象被销毁时自动调用
b、python语句建议不要在对象销毁时做任何事情,因为对象销毁的时间难以确定
语法
class 类名(继承列表):
def __del__(self):
语句块
3、预置的实例属性
1)__dict__属性
用来绑定一个存储此实例自身变量的字典
示例
class Dog:
pass
dog1 = Dog()
print(dog1.__dict__) # {}
dog1.color = "白色"
print(dog1.__dict__) # {'color': '白色'}
2)__class__ 属性
用来绑定创建此实例的类(类实例)
作用
可以借助于此属性来访问创建此实例的类示例
class Dog:
pass
dog1 = Dog()
print(type(dog1))
print(dog1.__class__)
dog2 = dog1.__class__()
print(dog2.__class__)
4、类变量
类变量是类的属性,此属性属于类,不属于此类创建的实例
作用
通常用来存储该类对象共有的数据
说明
a、类变量可以通过类直接访问
b、类变量可以通过类的实例直接访问
c、类变量可以通过此类的对象的 __class__属性间接访问
语法
class 类名(继承列表):
类变量名 = 表达式
...
示例
# 此示例示意类变量的用法,及类和对象的关系
class Human:
total_count = 0 # 类变量,此变量用来记录所有对象的个数
def __init__(self, n):
self.name = n
# 类变量可以通过类直接访问
print("Human类内的类变量Human.total_count=",Human.total_count)
Human.total_count += 1
print("Human.total_count=", Human.total_count)
# 类变量可以通过类的实例直接访问
h1 = Human('小张')
print("h1.total_count=", h1.total_count)
h1.total_count = 100 # 此做法是为实例添加一个变量,并不是修改类变量
print('Human.total_count=', Human.total_count) # 1
# 类变量可以通过此类的对象的 __class__属性间接访问
h1.__class__.total_count = 200
print("Human.total_count=", Human.total_count) # 200
5、类的文档字符串(__doc__属性)
类内没有赋值给任何变量的字符串为类的文档字符串类的文档字符串由类的__doc__属性绑定
示例
class Dog:
'''这是类的文档字符串'''
pass
查看方法:(交互模式下)
>>> help(Dog)
>>> dog1 = Dog()
>>> help(dog1)
6、__slots__ 属性
作用限定一个类创建的实例只能有固定的属性(实例变量),不允许对象添加列表以外的属性
访止用户因错写属性的名称而发生程序错误
说明
__slots__ 属性是一个列表,列表的值是字符串
含有__slots__属性的类所创建的实例没有__dict__属性,即此实例不用字典来存储对象的属性
示例
class Student:
# 此列表让Student创建的对象只能用name和 age属性,不能有其它属性
__slots__ = ['name', 'age']
def __init__(self, name, age):
self.name = name
self.age = age
s1 = Student('小张', 15)
print(s1.__dict__) # 报错,因为没有__dict__字典
print(s1.age) # 15
s1.Age = 16 # 报错,不允添加__slots__列表以外的属性
print(s1.age) # 16?
7、 __base__ 属性
类的__base__属性用来记录此类的基类内建类的继承关系
>>> help(__builtins__)
示例
class Human:
pass
class Student(Human):
pass
class Teacher(Human):
pass
Student.__base__ is Human # True
二、方法
1、实例方法(method)
作用
用于描述一个对象的行为,让此类型的全部对象都拥有相同的行为
说明
实例方法的实质是函数,是定义在类内的函数
实例方法至少有一个形参,第一个形参代表调用这个方法的实例,一般命名为'self'
实例方法的调用语法
实例.实例方法名(调用传参)
或
类名.实例方法名(实例, 调用传参)
语法
class 类名(继承列表):
def 实例方法(self, 形参1, 形参2, ...):
'''方法的文档字符串'''
语句块
例子
class Dog:
'''这是自定义的类, 用来描述小动物行为'''
def eat(self, food):
'''小狗吃东西的行为...'''
print("小狗正在吃", food)
self.food = food
def food_info(self):
print("上次吃的是", self.food)
dog1 = Dog() # 创建一个实例对象
dog1.eat('骨头') # 让狗吃东西
dog2 = Dog() # 创建一个实例对象
Dog.eat(dog2,'包子') # 让狗吃东西
dog1.food_info()
Dog.food_info(dog2)
2、类方法@classmethod
类方法是用于描述、操作类的行为的方法,此方法属于类,不属于该类创建的实例
说明
a、类方法需要使用@classmethod 装饰器定义
b、类方法至少有一个形参,第一个形参用于绑定类,约定写为'cls'
c、类和对象实例都可以调用类方法
d、类方法不能访问此类创建的对象的属性
语法
class A:
V = 0
@classmethod
def get_v(cls): # 此方法不是实例方法,是类方法
return cls.v
示例
3、静态方法 @staticmethod
静态方法是定义在类内的函数,此函数的作用域是类的内部
说明
a、静态方法需要使用staticmethod装饰器定义
b、静态方法与普通函数定义相同,不需要传入self实例参数和cls类参数
c、静态方法只能凭借该类或类的实例调用
d、静态方法不能访问类变量和实例变量(属性)
示例
class A:
@staticmethod
def myadd(x, y):
return x + y
print(A.myadd(100, 200)) # 300
a = A()
print(a.myadd(300, 400)) # 700
三、函数
1、isinstance(obj, class_or_tuple)
返回这个对象obj 是否是某个类的对象,或者某些类中的一个类的对象,如果是则返回True,否则返回False2、type(obj)
返回对象的类3、示例
isinstance(3.14, float) # True
isinstance('hello', str) # True
class Dog:
pass
dog1 = Dog()
isinstance(dog1, Dog) # True
isinstance(dog1, (int, flaot)) # False
isinstance(dog1, (int, flaot, Dog)) # True
type(dog1) is Dog # True
4、issubclass(cls, class_or_tuple)
判断一个类是否继承自其它的类,如果此类cls是class或tuple中的一个派生子类,则返回 True, 否则返False
示例
class A:
pass
class B(A):
pass
class C(B):
pass
print(issubclass(C, B)) # True
print(issubclass(B, C)) # False
print(issubclass(bool, (C, B, A, int))) # True
5、super 函数
返回绑定超类的实例,用超类的实例来调用其父类的覆盖方法说明
super(type, obj) 返回绑定超类的实例(要求obj必须为type类型的实例)
super() 返回绑定超类的实例,等同于 super(__class__, 实例方法的第一个参数),且必须在方法内调用
四、特性
1、封装 enclosure
封装是指隐藏类的实现细节,让使用者不用关心这些细节
封装的目的是让使用者通过尽可能少的方法(或属性)操作对象
私有属性和方法
python类中以双下划线('__')开头,不以双下划线结尾的标识符为私有成员,私有成员只能用此类的方法进行访问和修改,不能在子类或其他地方使用
示例
class A:
def __init__(self):
self.__p1 = 100 # 创建私有属性,此属性在类外无法访问
def __m1(self): # 私有方法
print("__m1 私有方法被调用")
def infos(self):
print("A类的infos访问的__p1属性是:", self.__p1)
self.__m1() # 调用自己的私有方法
a = A()
# print(a.__p1) # 出错
a.infos()
# a.__m1() # 出错 当前主模块不能调用A类的私有方法
2、继承(inheritance) 和 派生 (derived)
继承是从已有的类中派生出新的类,新类具有原类的数据属性和行为,并能扩展新的行为
派生就是从一个已有的类衍生出新的类,在新的类上可以添加新的属性和行为
作用
a、用继承派生机制,可以将一些共有功能加在基类中,实现代码共享.b、在不改变超类的代码的基础上改变原有的功能
名词
派生类(derived class) / 子类(child class)
说明
任何类都直接可间接的继承自object类object类是一切类的超类
注:类的__base__属性用来记录此类的基类
1)覆盖 override
覆盖是指在有继承关系的类中,子类中实现了与基类(超类)同名的方法,在子类实例调用该方法时,实际调用的是子类中的覆盖版本的方法的现象叫覆盖
例子
#示例 B继承A,work覆盖父类中的
class A:
'''A类'''
def work(self):
print("A.work被调用!")
class B(A):
'''B类'''
def work(self):
'''work 方法覆盖了父类的work'''
print("B.work被调用!")
b = B()
b.work() # B.work
a = A()
a.work() # A.work
问题:当覆盖发生时,子类对象能否调用父类中的方法?实现方法:b.__class__.__base__.work(b) # A.work
super 函数
返回绑定超类的实例,用超类的实例来调用其父类的覆盖方法说明
super(type, obj) 返回绑定超类的实例(要求obj必须为type类型的实例)
super() 返回绑定超类的实例,等同于 super(__class__, 实例方法的第一个参数), 且必须在方法内调用
示例
# 此示例示意用super构造函数来间接调用父类的覆盖版本的方法
class A:
def work(self):
print("A.work()")
class B(A):
def work(self):
print("B.work()")
def super_work(self):
'''此方法先调用一下子类的work,
再调用一下父类的work'''
self.work() # 调用自己的work
# super(B, self).work() # 调用父类的work
super().work() # 调用父类的work
b = B()
# b.work() # B.work(),
# super(B, b).work() # A.work()
b.super_work()
# super().work() # 错的!!! super() 不能在方法使用
super函数显式调用基类的构造方法
当子类实现了__init__方法后,父类的__init__方法将被覆盖(即不会主动调用父类的__init__方法),会引起父类的属性得不到初始化,此时需要显式调用父类的初始化方法。
示例
class Human:
def __init__(self, n, a):
self.name = n
self.age = a
print("Human.__init__被调用")
def infos(self):
print("姓名:", self.name)
print("年龄:", self.age)
class Student(Human):
def __init__(self, n, a, s):
super().__init__(n, a) # 显式调用父类的初始化方法
self.score = s
print("Student.__init__被调用")
def infos(self):
super().infos()
print("成绩:", self.score)
# h1 = Human('小张', 18)
# h1.infos()
s1 = Student("张学友", 35, 60)
s1.infos()
注意:super函数显式调用基类的构造方法时,super().__init__(...)必须紧跟在子类的__init__(...): 之后
2)单继承
语法
class 类名(基类名):
语句块
说明
单继承是指派生类由一个基类衍生出来
示例
# 此示例示意单继承的语句及定义方法
class Human:
'''此类用于描述人类的共性行为'''
def say(self, what): # 说话的行为
print("say:", what)
def walk(self, distance): # 走路的行为
print("走了", distance, '公里')
class Student(Human):
def study(self, subject):
print("正在学习", subject)
class Teacher(Student):
def teach(self, subject):
print("正在教:", subject)
h1 = Human()
h1.say('今天天气真好')
h1.walk(5)
print('-----------------')
s1 = Student()
s1.walk(4)
s1.say("走的有点累")
s1.study('python')
print('---------------')
t1 = Teacher()
t1.say("今天晚饭吃什么?")
t1.walk(3)
t1.teach("继承/派生")
t1.study("魔方")
3)多继承 multiple inheritance
多继承是指一个子类继承自两个或两个以上的基类说明
a、一个子类同时继承自多个父类,父类中的方法可以同时被继承下来
b、如果两个父类中有同名的方法,则在子类中又没有覆盖,此方法时,调用结果难以确定
语法
class 类名(基类名1, 基类名2, ...)
语句块
示例
# 此示例示意多继承的语法和用法
class Car:
def run(self, speed):
print('车正在以', speed, '公里/小时的速度行驶')
class Plane:
def fly(self, height):
print('飞机以海拔', height, '米的高空飞行')
class PlaneCar(Plane, Car):
'''PlaneCar类同时继承是Plane和 Car类'''
p1 = PlaneCar()
p1.fly(10000)
p1.run(300)
多继承的问题(缺陷)
标识符(名字空间)冲突的问题要谨慎使用多继承
解决方案:多继承的 MRO (Method Resolution Order)问题MRO 方法搜索顺序问题 示例:
python 3 广度优先
python 2 深度优先
多继承的支持语言
支持: C++ / Python3
不支持多继承:Java / Objective-C / Swift / C#
3、多态 polymorphic
字面意思:多种状态
多态是指在有继承/派生关系的类中,调用基类对象的方法,实际能调用子类的覆盖方法的现象叫多态。
说明
a、多态调用方法与对象相关,不与类相关
b、python的全部对象只有“运行时状态(动态)”,没有“C++/Java”里的“编译时状态(静态)”
示例
class Shape:
'''图形'''
def draw(self):
print("Shape的draw() 被调用")
class Point(Shape):
def draw(self):
print("正在画一个点")
class Circle(Point):
def draw(self):
print("正在画一个圆")
def my_draw(s):
s.draw() # s.draw调用谁是在运行时由s的类型动态决定
# 此处显示出运行时状态
shape1 = Circle()
shape2 = Point()
my_draw(shape1)
my_draw(shape2)
用途
不同的对象有相同的行为(方法),需要采用多态
五、附录
六、习题集
1、练习1(单继承)
list类里只有append想末尾添加一个元素的方法,但是没有向列表头部添加元素的方法,事项能否为列表在不改变原有功能的基础上添加一个insert_head(n)的方法,在列表的头部添加元素。
class Mylist(list):
def insert_head(self, element)
self.insert(0,element)
myl = Mylist(range(3, 6))
print(myl) # 【3, 4, 5】
myl.insert_head(2) # 添加2到首部
print(myl) # 【2, 3, 4, 5】