Python面向对象
- 思想
- 用对象的角度描述世界
- 以模块化思想解决工程问题
- 常用名词
- OO:面向对象(Object-Oriented)
- OOA:面向对象分析(Object-Oriented Analysis)
- OOD:面向对象设计(Object-Oriented Design)
- OOP:面向对象编程(Object-Oriented Orogramming)
- OOI:面向对象实现(Object-Oriented Implementation)
类&对象
类:抽象,描述的是一类共性事物的集合
对象:具象,描述的是某一类事物中的单个个体
- 类定义
- 格式:
class ClassName:
- 类名使用大驼峰命名法
- 类中的函数一般需要
self
作为参数 - 类包含属性(变量)和方法(函数)
- 属性:事物的特征
- 方法:事物的功能
- 格式:
- 对象实例化
- 格式:
object_name = ClassName()
- 对象名一般使用posix命名法
- 访问对象中的变量或调用函数时,使用
object_name.var/func()
- 格式:
- 可以通过默认内置变量检查类和对象的所有成员
- 格式:
ClassName/obj_name.__dict__
- 返回值:类或对象的成员信息,dict类型
- 格式:
- 类在定义之后,可以不实例化对象,直接用类实例访问成员
- 格式:
ClassName.var/func()
- 格式:
# 类定义案例
class Student(object):
# pass用于占位,无意义
pass
# 类定义和对象实例化案例
class PythonStudent(object):
name = 'Madman'
age = 18
course = 'Python'
def learn_python(self):
print('Learning Python...')
return None
madman = PythonStudent()
print(madman)
print(madman.name)
print(madman.age)
print(madman.course)
madman.learn_python()
"""
运行结果:
<__main__.PythonStudent object at 0x000001FBEBF0DDA0>
Madman
18
Python
Learning Python...
"""
# 查看类和对象所有成员案例
class Person(object):
name = 'no_name'
age = 0
def say_hello(self):
print('Hello,World')
print(type(Person.__dict__))
print(Person.__dict__)
person = Person()
print(type(person.__dict__))
print(person.__dict__)
person.name = 'Madman'
person.age = 23
print(type(person.__dict__))
print(person.__dict__)
"""
运行结果:
<class 'mappingproxy'>
{'__module__': '__main__', 'name': 'no_name', 'age': 0, 'say_hello': <function Person.say_hello at 0x00000191156357B8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
<class 'dict'>
{}
<class 'dict'>
{'name': 'Madman', 'age': 23}
"""
类&对象成员分析
- 类和对象都可以存储对象,成员可以归类所有,也可以归对象所有
- 类存储成员时使用的是与类关联的一个对象
- 对象存储成员时是存储在当前对象
- 对象访问一个成员时,如果对象中没有该成员,就会尝试访问类中的同名成员,如果对象中有此成员,一定使用对象中的成员
- 创建对象时,类中的成员不会放入对象当中,而是得到一个空对象,没有成员
- 对象对类中成员重新赋值或者对象添加成员时,对应成员会保存在对象中,而不会修改类中的成员
self
- self不是关键字
- self可以用别的名称代替
- 作用是指代自己本身
- 基本所有的类方法都会带self,这样子能方便使用类自身的成员:
self.var/func()
- 如果通过对象调用一个方法,那么该对象会自动传入到当前方法的第一个参数中,如果通过类实例调用一个方法,则默认不传入任何参数,如果想要传入自身,需要手动将self传入:
ClassName.func(self)
- 有self形参的方法称为非绑定类的方法,可以通过对象访问,也可以通过类实例传入参数的方法访问;没有self形参的是绑定类的方法,只能通过类实例访问
# self案例1
class Person(object):
def say_hi(self):
print('Hello!')
return None
madman = Person()
# 调用时,实际上默认实例作为第一个参数传入
madman.say_hi()
# 在已传入参数的情况下再传入一个参数,报错
madman.say_hi('haha')
"""
运行结果:
Hello!
TypeError: say_hi() takes 1 positional argument but 2 were given
"""
# self案例2
class Person(object):
name = 'madman'
def say_hi(haha):
print('Hello! I am {0}'.format(haha.name))
return None
madman = Person()
madman.say_hi()
"""
运行结果:
Hello! I am madman
"""
# self案例3
class Person(object):
def say_hi(self):
print('Hello,World')
def say_hi_again():
print('Hello,World!')
person = Person()
person.say_hi()
# 默认传入了一个参数self
person.say_hi_again()
# 类实例默认不传入参数
Person.say_hi()
# 手动传入自身参数不报错
Person.say_hi(self)
Person.say_hi_again()
# 强行传入自身参数报错
Person.say_hi_again(self)
"""
运行结果:
Hello,World
TypeError: say_hi_again() takes 0 positional arguments but 1 was given
TypeError: say_hi() missing 1 required positional argument: 'self'
NameError: name 'self' is not defined
Hello,World!
NameError: name 'self' is not defined
"""
# 鸭子模型案例
class Dog(object):
name = 'NoNameDog'
age = 0
def __init__(self):
self.name = 'little dog'
self.age = 2
def say_hi(self):
print('I am {0}, I am {1} years old.'.format(self.name, self.age))
class Cat(object):
name = 'NoNameCat'
age = 0
dog = Dog()
# 此时,系统会默认把dog作为第一个参数c传入构造函数
dog.say_hi()
# 此时,self被dog对象替换
Dog.say_hi(dog)
# 此时,类实例Dog作为参数传入
Dog.say_hi(Dog)
# 此时,传入了类实例Cat
Dog.say_hi(Cat)
# 此时会报错
Dog.say_hi()
"""
运行结果:
I am little dog, I am 2 years old.
I am little dog, I am 2 years old.
I am NoNameDog, I am 0 years old.
I am NoNameCat, I am 0 years old.
TypeError: say_hi() missing 1 required positional argument: 'self'
"""
类属性和对象属性问题
- 访问类属性格式:
ClassName.var_name
- 对象可以使用类的属性
- 实例化一个对象时,实际上对象对类的属性进行了浅拷贝(即仅拷贝了类中变量的地址,而不拷贝值)
# 类属性和对象属性问题案例1
class Student(object):
name = 'madman'
age = 23
def say_hi(self):
print('My name is {0}, I am {1} years old.'.format(self.name, self.age))
print(id(self.name), id(self.age))
return None
# 查看类属性
print(Student.name, id(Student.name))
print(Student.age, id(Student.age))
madman = Student()
madman.say_hi()
# 实际上指向类的属性
print(id(madman.name), id(madman.age))
spiderman = Student()
spiderman.say_hi()
# 同样指向类的属性
print(id(spiderman.name), id(spiderman.age))
"""
运行结果:
madman 1855125198416
23 140726619730368
My name is madman, I am 23 years old.
1855125198416 140726619730368
1855125198416 140726619730368
My name is madman, I am 23 years old.
1855125198416 140726619730368
1855125198416 140726619730368
"""
- 实例化的对象可以通过重新传入新的属性值来改变自身的属性值,但不改变类的属性(类属性和对象属性是两回事)
- 再次证明实例化对象中属性实际上是对类中属性的浅拷贝
- 如果对不同对象赋相同的属性值,他们的id一致(Python缓存机制)
# 类属性和对象属性问题案例2
class Student(object):
name = 'madman'
age = 23
def say_hi(self, name, age):
self.name = name
self.age = age
print('My name is {0}, I am {1} years old.'.format(self.name, self.age))
print(id(self.name), id(self.age))
return None
# 查看类属性
print(Student.name, id(Student.name))
print(Student.age, id(Student.age))
print('*' * 20)
# 对对象属性重新赋值
madman = Student()
madman.say_hi('badman', 18)
print(id(madman.name), id(madman.age))
print('*' * 20)
# 对象属性改变后,类属性仍不变
print(Student.name, id(Student.name))
print(Student.age, id(Student.age))
print('*' * 20)
# 如果给另一个对象赋相同的值,id是一样的
badman = Student()
badman.mame = 'badman'
badman.age = 19
print(badman.mame, id(badman.mame))
print(badman.age, id(badman.age))
"""
运行结果:
madman 2586910332496
23 140726554259904
********************
My name is badman, I am 18 years old.
2586912611720 140726554259744
2586912611720 140726554259744
********************
madman 2586910332496
23 140726554259904
********************
badman 2586912611720
19 140726554259776
"""
访问类的属性
- 在类中只允许使用类的内容
- 类访问不了对象的内容,对象能访问类的内容
- 在类方法中如果强制访问类的属性,有两种方法
- 方法一:
__class__.var_name
- 方法二:
ClassName.var_name
- 方法一:
# 访问类的属性案例
class Person(object):
name = 'madman'
age = 23
local = 'Shenzhen'
# 三种方式访问属性,第一种访问对象属性,第二第三种访问类属性
def say_hi1(self):
print('Hello! I am {0}, I am {1} years old and I live in {2}'.format(self.name, Person.age, __class__.local))
print(id(self.name), id(Person.age), id(__class__.local))
return None
# 错误示范
def say_hi2():
print('Hello! I am {0}, I am {1} years old and I live in {2}'.format(self.name, Person.age, __class__.local))
print(id(this.name), id(Person.age), id(__class__.local))
return None
madman = Person()
madman.say_hi1()
print('*' * 20)
madman.say_hi2()
print('*' * 20)
badman = Person()
badman.name = 'badman'
badman.age = 18
badman.local = 'Huizhou'
badman.say_hi1()
"""
运行结果:
Hello! I am madman, I am 23 years old and I live in Shenzhen
2656137896768 140726553670080 2656137929328
********************
TypeError: say_hi2() takes 0 positional arguments but 1 was given
********************
Hello! I am badman, I am 23 years old and I live in Shenzhen
2656137896992 140726553670080 2656137929328
"""
构造函数
一种特殊的函数,类在实例化对象的时候,执行一些基础性的初始化工作
- 使用特殊的名称和写法
- 定义格式:
def __init__(self):
- 注意
- 第一个参数必须要有,一般推荐用self
- 不能有return
- 定义格式:
- 在实例化对象的时候,自动执行构造函数,且构造函数是第一个被执行的函数
# 构造函数案例
class Student(object):
name = 'NoName'
age = 0
def __init__(self):
print('我是构造函数')
madman = Student()
print(madman.name)
print(madman.age)
"""
运行结果:
我是构造函数
NoName
0
"""
面向对象的三大特征
封装、继承、多态
封装
对类的成员进行访问限制
- 封装的三个级别
- public(公有)
- 公有的封装实际对成员没有任何操作,任何地方都可以访问
- 定义公有变量格式:
var_name = vat_value
- protected(保护)
- 受保护的封装是将对象成员进行一定级别的封装,在类或者子类中都可以进行访问,但在外部不可以
- 定义保护变量格式:
_var_name = vat_value
(一个下划线)
- private(私有)
- 最高级别的封装,只能在当前类或对象中访问
- 定义私有变量格式:
__var_name = var_value
(两个下划线) - Python的私有不是真的私有,是一种称为name mangling的改名策略
- 访问私有变量格式:
object_name._ClassName__var_name
- 注意:public、protected、private不是关键字
- public(公有)
# 私有案例1
class Person(object):
name = 'Madman'
__age = 23
def my_age(self):
print(self.age)
person = Person()
print(person.name)
# 不能访问私有成员
print(person.__age)
# 但可以通过类内部访问
person.my_age()
print(Person.name)
# 类实例依然不能访问私有成员
print(Person.__age)
Person.my_age(Person)
"""
运行结果:
Madman
AttributeError: 'Person' object has no attribute '__age'
23
Madman
AttributeError: type object 'Person' has no attribute '__age'
23
"""
# 私有案例2
class Person(object):
__age = 23
person = Person()
# 可以通过object_name._ClassName__var_name访问私有变量
print(person._Person__age)
# 直接访问就会失败
print(person.__age)
"""
运行结果:
23
AttributeError: 'Person' object has no attribute '__age'
"""
继承
一个类可以获得另一个类的所有成员
- 作用
- 减少代码量
- 增加代码复用性
- 设置类与类的关系
- 继承与被继承
- 继承与被继承是从属关系
- 父类(基类):被子类继承的类,可以理解为超集
- 子类(派生类):有继承行为的类,可以理解为子集
- 继承的特征
- 所有的类都继承自object类,即所有的类都是object类的子类
- 子类一旦继承父类,则可以使用父类中除私有成员外的所有内容
- 子类中可以定义独有的成员属性和方法
- 子类继承父类后并没有将父类成员完全赋值到子类中,而是通过引用关系访问调用
- 如果子类和父类之间有相同的属性名或行为名(包括构造函数),在实例化子类对象时优先使用子类的属性或行为
- 继承类的格式:
class SubClass(ParentClass)
# 继承案例1
# 默认继承object
class Animal1:
pass
class Animal2(object):
pass
# 继承案例2
class Person(object):
name = 'NoName'
_age = 0
__local = 'NoLocal'
# 父类写在类定义中的括号里
class Teacher(Person):
pass
t = Teacher()
# 子类可以继承父类的公有成员
print(t.name)
# 子类可以继承父类的保护成员
print(t._age)
# 但子类不可以继承父类的私有成员
print(t._Teacher__local)
"""
运行结果:
NoName
0
AttributeError: 'Teacher' object has no attribute '_Teacher__local'
"""
多继承
在Python中,子类允许有多个父类(多继承)
- 但不鼓励使用多继承
- 单继承和多继承的特点
- 单继承
- 优点:传承有序逻辑清晰语法简单隐患少
- 缺点:功能不能无限扩展,只能在当前唯一的继承链中扩展
- 多继承
- 优点:类的功能扩展方便
- 缺点:继承关系乱
- 单继承
- 当多个父类之间有相同的属性名或者行为名时,子类按照顺序优先的原则去继承(继承最先出现的)
# 多继承案例
class Person(object):
name = 'Person'
def Walking(self):
print('I can walking')
class Bird(object):
name = 'Bird'
def Flying(self):
print('I can flying')
# 继承两个父类
class BirdMan(Person, Bird):
pass
bm = BirdMan()
bm.Walking()
bm.Flying()
# 继承的父类间有相同的属性名,按照继承顺序优先的原则
print(bm.name)
"""
运行结果:
I can walking
I can flying
Person
"""
菱形继承/钻石继承问题
多个子类继承自同一个父类,而这些子类又被同一个类继承,继承关系图形成一个菱形图谱
# 菱形继承案例
class A(object):
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
多继承的MRO
MRO是在多继承中,用于保存继承顺序的一个列表
- Python本身采用C3算法来处理多继承的菱形继承问题
- MRO列表的计算原则
- 子类永远在父类前面
- 如果有多个父类,则根据继承语法中括号内类的书写顺序存放
- 如果多个类继承了同一个父类,孙子类中只会选取继承语法括号中第一个父类的父类
子类继承父类成员的查找顺序问题
- 优先查找本类的成员,如果本类中有定义,则使用本类的成员,如果本类没定义,则不断向上查找父类成员直到找到为止
# 子类继承父类成员的查找顺序问题案例
class Person(object):
name = 'NoName'
age = 0
class Madman(Person):
name = 'Madman'
madman = Madman()
print(type(madman.name))
print(madman.name)
print(type(madman.age))
print(madman.age)
"""
运行结果:
<class 'str'>
Madman
<class 'int'>
0
"""
issubclass()
检测两个类之间是否是父子关系
- 格式:
issubclass(SubClass, ParentClass)
- 返回值:布尔值
# issubclass()案例
class Person(object):
pass
class Bird(object):
pass
class BirdMan(Person, Bird):
pass
class SuperBirdMan(BirdMan):
pass
print(issubclass(BirdMan, Person))
print(issubclass(BirdMan, Bird))
print('*' * 20)
print(issubclass(Person, Bird))
print('*' * 20)
print(issubclass(Person, BirdMan))
print(issubclass(Bird, BirdMan))
print('*' * 20)
print(issubclass(SuperBirdMan, BirdMan))
print(issubclass(SuperBirdMan, Person))
print(issubclass(SuperBirdMan, Bird))
"""
运行结果:
True
True
********************
False
********************
False
False
********************
True
True
True
"""
isinstance()
检测一个对象是否是一个类的实例
- 格式:
isinstance(object_name, ClassName)
- 返回值:布尔值
# isinstance案例
class Person(object):
pass
person = Person()
print(isinstance(person, Person))
print(isinstance(Person, Person))
"""
运行结果:
True
False
"""
hasattr()
检测一个成员是否在类里面
- 格式:
hasattr(object_name, member_name)
- 返回值:布尔值
# hasattr案例
class Perosn(object):
name = 'Madman'
def say(self):
pass
person = Person()
print(hasattr(person, 'name'))
print(hasattr(person, 'age'))
print(hasattr(person, 'say'))
"""
运行结果:
True
False
True
"""
super
- super不是一个函数,而是一个类
- super的作用是获取MRO(MethodResolustionOrder)列表中的第一个类(往往是父类)
- super与父类没有任何实质性关系,但通过super调用父类
mro
- 用于查询某个类的父类
- 格式:
ClassName.__mro__
- 元组类型的数据,显示所有的父类
# __mro__案例
class Animal(object):
pass
class Dog(Animal):
pass
print(Animal.__mro__)
print(Dog.__mro__)
"""
运行结果:
<class 'tuple'>
(<class '__main__.Animal'>, <class 'object'>)
<class 'tuple'>
(<class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>)
"""
构造函数的继承
子类继承父类时,父类的构造函数默认被子类继承
- 如果子类定义了构造函数,则实例化对象时使用自身的构造函数,不查找父类构造函数
- 如果子类没定义构造函数,则按照MRO列表的顺序查找父类构造函数
- 如果子类没定义构造函数且父类的构造函数带参数,则实例化对象时的参数应该按照父类参数实例化
# 构造函数的继承案例1
class Person(object):
name = 'NoName'
def __init__(self):
print('My name is {0}'.format(self.name))
class Teacher(Person):
pass
t = Teacher()
"""
运行结果:
My name is NoName
"""
# 构造函数的继承案例2
class Person(object):
name = 'NoName'
age = 0
def __init__(self):
print('My name is {0}'.format(self.name))
class Teacher(Person):
age = 23
def __init__(self, name):
print('My name is {0}, I am {1} years old'.format(name, self.age))
p = Person()
# 子类和父类都有定义构造函数和相同的属性名,优先使用子类的构造函数和属性
t = Teacher('Madman')
"""
运行结果:
My name is NoName
My name is Madman, I am 23 years old
"""
# 构造函数的继承案例3
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
print('name: {0}, age: {1}'.format(self.name, self.age))
class Madman(Person):
pass
# 使用的是Person类的构造函数,所以不输入对应的参数会报错
madman = Madman()
madman = Madman(name='madman', age=23)
"""
运行结果:
TypeError: __init__() missing 2 required positional arguments: 'name' and 'age'
name: madman, age: 23
"""
子类扩充父类方法问题
- 子类如果想扩充父类的方法,可以在定义新方法的同时访问父类成员来进行代码重用,可以使用
ParentClassName.parent_class_member
或super().parent_class_member
格式来调用父类成员
# 子类扩充父类方法问题案例
class Person(object):
def work(self):
print('I can earn money!')
class Madman(Person):
def earn_money(self):
print('I can earn many money!!')
def work_hard(self):
# 用父类函数的功能进行扩充只需调用父类相应的函数
# 用ParentClassName.parent_class_member格式调用父类函数
# 注意要加self,因为类实例本身不自动传入参数
Person.work(self)
# 也可以用super().parent_class_member格式调用父类函数
super().work()
# 可以直接写代码扩充
print('I can earn many money!!')
# 也可以调用自身函数扩充
self.earn_money()
madman = Madman()
madman.work_hard()
"""
运行结果:
I can earn money!
I can earn money!
I can earn many money!!
I can earn many money!!
"""
多态
同一个对象在不同情况下有不同的状态出现
- 在Python中,多态不是语法,只是一种设计思想
- 多态性:同一种调用方式有不同的执行结果
- 多态:一类事物有多种形态
- Mixin设计模式
- 主要采用多继承方式对类的功能进行扩展
- Mixin的目的就是给一个类增加多个功能,这样在设计类的时候,我们优先考虑通过多重继承来组合多个Mixin的功能,而不是设计多层次的复杂的继承关系
- 使用Mixin实现多继承时需要注意
- 职责必须单一,如果有多个功能,则写多个Mixin
- Mixin不能依赖于子类的实现
- 子类即使没有继承这个Mixin类也照样能工作,只是缺了某个功能
- 优点
- 使用Mixin可以在不对类进行任何修改的情况下扩充功能
- 可以方便地组织和维护不同功能组件的划分
- 可以根据需要任意调整功能类的组合
- 可以避免创建很多新的类,导致类的继承混乱
# Mixin案例1(未遵循Mixin)
class Person(object):
def say(self):
print('I am a man')
class Student(Person):
def learn(self):
print('Learning')
class Teacher(Person):
def work(self):
print('Working')
class Tutor(Student, Teacher):
pass
tutor = Tutor()
print(Tutor.__mro__)
print(tutor.__dict__)
print(Tutor.__dict__)
"""
运行结果:
(<class '__main__.Tutor'>, <class '__main__.Student'>, <class '__main__.Teacher'>, <class '__main__.Person'>, <class 'object'>)
{}
{'__module__': '__main__', '__doc__': None}
"""
# Mixin案例1(遵循Mixin)
class Person(object):
def say(self):
print('I am a man')
class Student(object):
def learn(self):
print('Learning')
class Teacher(object):
def work(self):
print('Working')
class Tutor(Person, Student, Teacher):
pass
tutor = Tutor()
print(Tutor.__mro__)
print(tutor.__dict__)
print(Tutor.__dict__)
"""
(<class '__main__.Tutor'>, <class '__main__.Person'>, <class '__main__.Student'>, <class '__main__.Teacher'>, <class 'object'>)
{}
{'__module__': '__main__', '__doc__': None}
"""
property()
对属性的读、写、删除定义操作
- 有三种方法使用类的成员描述符
- 使用类实现描述器:适合多个类中的多个属性共用一个描述符
- 使用属性修饰符: 适合在类中使用,控制一个类中的一个属性
- 使用property函数:适用于当前类中,可以控制一个类中的多个属性
- 格式:
property(fget, fset, fdel, doc)
- fget:获取属性时的操作
- fset:修改或添加属性时的操作
- fdel:删除属性时的操作
- doc:说明文档
- 格式:
# property()案例
class Person(object):
# 定义读操作
def fget(self):
return self._name * 2
# 定义写操作
def fset(self, name):
self._name = name.upper()
# 定义删除操作
def fdel(self):
self._name = 'NoName'
name = property(fget, fset, fdel, '对name进行操作')
person = Person()
# 修改操作
person.name = 'Madman'
# 读操作
print(person.name)
"""
运行结果:
MADMANMADMAN
"""
类的内置属性
__dict__
:以字典的形式显示类的成员组成__doc__
:获取类的文档信息__name__
:获取类的名称,如果在模块中使用,则获取模块的名称__bases__
:获取某个类的所有父类,以元组的方式显示
# 类的内置函数案例
class Person(object):
'''
这是一个说明文档
'''
name = 'Madman'
age = 23
def __init__(self):
print('你好')
print(Person.__dict__)
print(Person.__doc__)
print(Person.__name__)
print(Person.__bases__)
"""
运行结果:
{'__module__': '__main__', '__doc__': '\n 这是一个说明文档\n ', 'name': 'Madman', 'age': 23, '__init__': <function Person.__init__ at 0x000001AB980657B8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>}
这是一个说明文档
Person
(<class 'object'>,)
"""
类的常用魔法函数
魔术方法就是不需要人为调用的方法,在特定的情况下自动触发
- 魔术方法的统一特征是方法名前后都有两个下划线
- 构造函数举例
- 操作类相关
__init__
:构造函数__new__
:对象实例化方法__call__
:对象被当作函数使用的时候触发__str__
:对象被当作字符串使用的时候触发
- 描述符相关
__set__
__get__
__delete__
- 属性操作相关
__getattr__
:访问一个不存在的属性时触发__setattr__
:对成员属性进行设置的时候触发- 参数
- self:用来获取当前对象
- 第二个参数是被设置属性的名称,以字符串形式出现
- 第三个参数是对属性设置的值
- 作用
- 进行属性设置的时候进行验证或修改
- 参数
__gt__
:进行大于判断时候触发的函数- 参数
- self
- 第二个参数是第二个对象名
- 返回值:可以是任意值,推荐返回布尔值
- 参数
- 操作类相关
# __call__案例
class A(object):
def __init__(self):
print('__init__:我被调用了')
class B(object):
def __init__(self):
print('__init__:我被调用了')
def __call__(self):
print('__call__:我被调用了')
a = A()
a()
b = B()
b()
"""
运行结果:
TypeError: 'A' object is not callable
__init__:我被调用了
__call__:我被调用了
"""
# __str__案例
class A(object):
def __init__(self):
print('__init__:我被调用了')
class B(object):
def __init__(self):
print('__init__:我被调用了')
def __str__(self):
print('__str__:我被调用了')
return '__str__:返回的内容'
a = A()
print(a)
b = B()
print(b)
"""
运行结果:
__init__:我被调用了
<__main__.A object at 0x000001D5240F08D0>
__init__:我被调用了
__str__:我被调用了
__str__:返回的内容
"""
# __getattr__案例
class A(object):
name = 'NoName'
def __init__(self):
print('__init__:我被调用了')
class B(object):
name = 'NoName'
def __init__(self):
print('__init__:我被调用了')
def __getattr__(self, age):
print('__getattr__:我被调用了')
a = A()
print(a.name)
print(a.age)
b = B()
print(b.name)
print(b.age)
"""
运行结果:
__init__:我被调用了
NoName
AttributeError: 'A' object has no attribute 'age'
__init__:我被调用了
NoName
__getattr__:我被调用了
None
"""
# __setattr__案例
class Person(object):
def __setattr__(self, name, value):
print('设置属性:{0}'.format(name))
# 下面语句会导致死循环
# self.name = value
# 这种情况下,为了避免死循环,规定统一调用父类魔法函数
super().__setattr__(name, value)
person = Person()
print(person.__dict__)
person.age = 18
"""
运行结果:
{}
设置属性:age
"""
# __gt__案例
class A(object):
def __init__(self, name):
self._name = name
a1 = A('one')
a2 = A('two')
print(a1 > a2)
class B(object):
def __init__(self, name):
self._name = name
def __gt__(self, obj):
print('{0}比{1}大吗?'.format(self, obj))
return self._name > obj._name
b1 = B('one')
b2 = B('two')
print(b1 > b2)
"""
运行结果:
TypeError: '>' not supported between instances of 'A' and 'A'
one比two大吗?
False
"""
类和对象的三种方法
- 实例方法
- 需要实例化对象才能使用的方法,使用过程中可能需要借助对象的其他对象的方法完成
- 静态方法
- 不需要实例化,通过类直接访问
- 类方法
- 不需要实例化
# 三种方法的案例
class Person(object):
# 实例方法
def eat(self):
print(self)
print('Eating...')
# 类方法
# 类方法的第一个参数一般命名为cls,区别于self
@classmethod
def play(cls):
print(cls)
print('Playing...')
# 静态方法
# 不需要用第一个参数表示自身或类
@staticmethod
def say():
print('Saying...')
madman = Person()
# 实例方法
Person.eat('eating')
madman.eat()
# 类方法
Person.play()
madman.play()
# 静态方法
Person.say()
madman.say()
"""
运行结果:
eating
Eating...
<__main__.Person object at 0x000001CACCBB4668>
Eating...
<class '__main__.Person'>
Playing...
<class '__main__.Person'>
Playing...
Saying...
Saying...
"""
抽象类
包括抽象方法的类叫抽象类
- 抽象类的使用需要借助abc模块
- 抽象方法:没有具体实现内容的方法
- 抽象方法的主要作用是规范子类的行为和接口
- 抽象类的主要作用是设定类的标准,让开发具有统一的规范
- 抽象的使用
- 抽象类可以包含抽象方法,也可以包含具体方法
- 抽象类中可以有方法也可以有属性
- 抽象类不允许直接实例化
- 必须继承才可以使用,且继承的子类必须实现所有继承来的抽象方法
# 抽象类的实现案例
import abc
# 声明一个类并指定当前类的元类
class Human(metaclass=abc.ABCMeta):
# 定义普通抽象方法
@abc.abstractmethod
def smoking(self):
pass
# 定义类抽象方法
@abc.abstractmethod
def drink():
pass
# 定义静态抽象方法
@abc.abstractstaticmethod
def play():
pass
自定义类
- 自定义类的方式
- 通过定义函数和类,并把函数赋值给类
- 借助MethodType()实现
- 借助type()实现
- 利用元类实现(MetaClass)
- 元类是类
- 元类用于创造别的类
# 函数名可以当变量使用案例
def say_hello(name):
print('{0},你好!'.format(name))
say_hello('Madman')
say_hi = say_hello
say_hi('Madman')
"""
运行结果:
Madman,你好!
Madman,你好!
"""
# 类的组装案例1
class A(object):
pass
def say(self):
print('Saying...')
A.say = say
a = A()
a.say()
"""
运行结果:
Saying...
"""
# 类的组装案例2
from types import MethodType
class A():
pass
def say(self):
print('Saying...')
a = A()
a.say = MethodType(say, A)
a.say()
"""
运行结果:
Saying...
"""
# 类的组装案例3
def say(self):
print('Saying...')
def talk(self):
print('Talking...')
A = type('A', (object, ), {'say': say, 'talk': talk})
a = A()
a.say()
a.talk()
"""
运行结果:
Saying...
Talking...
"""
# 类的组装案例4
# 元类的写法是固定的,它必须继承自type
# 元类命名一般以MetaClass结尾
class AMetaClass(type):
def __new__(cls, name, bases, attrs):
print('我是元类')
attrs['name'] = 'Madman'
return type.__new__(cls, name, bases, attrs)
# 元类定义完就可以使用了
class B(object, metaclass=AMetaClass):
pass
b = B()
print(b.name)
"""
运行结果:
我是元类
Madman
"""