面向对象编程介绍
面向过程编程
核心是“过程”二字,过程指的是做事情的步骤,即先做什么再做什么。
基于该编程思想写程序,就好比一条工厂流水线,一种机械式的思维方式。
优点:逻辑清晰,复杂的问题流程化,进而简单化。
缺点:可扩展性差。
面向对象编程
面向对象编程的核心是对象二字,对象指得是特征与技能的结合体。基于该编程思想写程序,就好比在创造世界,一种上帝式的思维方式。
优点:可扩展性高。
缺点:编写程序的复杂程度远高于面向过程。
类和对象
什么是类?类型,分类
先定义类,后调用类产生对象。
- 在现实世界中:对象是一个个具体存在的事物,类是由人类文明的发展抽象总结出来的。
- 在程序中:必须遵循先有类,再有对象
总结:
对象是特征与技能的结合体,类是一系列对象相同的特征与技能的结合体。
定义类
1 2 3 4 5 6 7 8
| class Student: school = 'oldboy' def choose_course(self): print('choose course') def learn(self): print('learn')
|
在定义类发生的事情:
- 类在定义时,会产生一个空的名称空间
- 会把类中定义的名字,扔进类的名称空间中
注意:类在定义阶段就已经产生好了名称空间,执行python文件时会执行类内部的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Student: school = 'oldboy' def choose_course(self): print('choose course') def learn(self): print('learn') print(Student.__dict__)
''' {'__module__': '__main__', 'school': 'oldboy', 'choose_course': <function Student.choose_course at 0x0000010EF2FD3510>, 'learn': <function Student.learn at 0x0000010EFA1A0950>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None} '''
|
对象的创建和使用
类的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Student: school = 'oldboy' def choose_course(self): print('choose course') def learn(self): print('learn') print(Student.school)
Student.school = 'oldgirl' print(Student.school) ''' oldgirl '''
Student.address = '上海' print(Student.address)
del Student.address
Student.choose_course(123)
|
名称空间的产生:
- 类的名称空间在定义阶段就已经产生了
- 对象的名称空间,在调用类时产生
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Student(): school = 'oldboy'
def choose_course(self): print('choose course')
def learn(self): print('learn')
stu1 = Student() stu2 = Student() stu3 = Student()
print(stu1.school) stu1.choose_course() stu1.learn()
|
在类内部定义__init__
函数
__init__
会在调用类时自动触发该函数,为对象初始化某些属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Student(): school = 'oldboy'
def __init__(self): print('此处是__init__')
def choose_course(self): print('choose course')
def learn(self): print('learn')
stu1 = Student() stu2 = Student() stu3 = Student()
print(stu1.school) stu1.choose_course() stu1.learn()
|
调用类发生的事情:
- 首先会产生一个空的对象,就是产生“对象的名称空间”
- 会自动触发
__init__
- 会把对象以及括号内的参数一并传给
__init__
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Student(): school = 'oldboy'
def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex
def choose_course(self): print('choose course')
def learn(self): print('learn')
stu1 = Student('cwz', 23, 'male')
print(stu1.name) print(stu1.age) print(stu1.sex)
''' cwz 23 male '''
|
对象与类的查找顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Student(): school = 'aaa'
def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex
def choose_course(self): print('choose course')
def learn(self): print('learn')
stu1 = Student('cwz', 23, 'male')
print(stu1.__dict__) print(stu1.name, stu1.school) ''' {'name': 'cwz', 'age': 23, 'sex': 'male'} cwz aaa '''
|
对象与类的查找顺序:
- 对象.属性,若对象本身有,则优先查找对象自己的
- 若对象本身没有,则去类里面找,若类里面没有,则报错
对象绑定方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Student(): school = 'oldboy'
def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex print(self)
def choose_course(self): print('choose course')
def learn(self): print('learn')
stu1 = Student('qwe', 20, 'male') stu1.learn()
|
类内部的函数主要是给对象用的:
- 由类来调用类内部的函数,该函数只是一个普通的函数,普通函数需要接收几个参数就得传入几个参数
- 由对象来调用称之为对象的绑定方法,不同的对象调用该绑定方法,则会将不同的对象传入该绑定方法中
- 对象的绑定方法,是由对象来调用的,特殊之处就是把对象当作第一个参数传入该方法中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| class Student(): school = 'oldboy'
def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex print('stu:', self)
def choose_course(self): print('choose course')
def learn(self): print('learn')
stu1 = Student('qwe', 20, 'male') stu2 = Student('cwz', 21, 'male') stu1.learn() print('stu1', stu1) stu2.learn() print('stu2', stu2) print(stu1.learn)
''' stu: <__main__.Student object at 0x0000019E249E2128> stu: <__main__.Student object at 0x0000019E249E2198> learn stu1 <__main__.Student object at 0x0000019E249E2128> learn stu2 <__main__.Student object at 0x0000019E249E2198> <bound method Student.learn of <__main__.Student object at 0x0000019E249E2128>> '''
|
小练习
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| ''' 人狗互咬大战 '''
class Person: def __init__(self, name, aggr, life): self.name = name self.aggr = aggr self.life = life
def bite(self, dog): if dog.life < 0: return True dog.life -= self.aggr
print( f''' 人 {self.name}咬狗 {dog.name} 狗掉血 {self.aggr} 狗还剩的血量 {dog.life} ''' )
class Dog: def __init__(self, name, dog_type, aggr, life): self.name = name self.dog_type = dog_type self.aggr = aggr self.life = life
def bite(self, person): if person.life < 0: return True person.life -= self.aggr
print( f''' 狗 {self.name}咬人 {person.name} 人掉血 {self.aggr} 人还剩的血量 {person.life} ''' )
p1 = Person('neo', 300, 400) d1 = Dog('大黄', '土狗', 200, 500)
while True: flag1 = p1.bite(d1) if flag1: break
flag2 = d1.bite(p1) if flag2: break
|
继承
什么是继承
继承指的是新建类的方法,新建的类称之为子类或派生类;
子类继承的类为父类,也称之为基类或超类。
继承的特征
子类可以继承父类的属性(特征与技能),并且可以派生出自己的属性(特征与技能)
为什么要继承
继承的目的是为了减少代码的冗余(减少代码量)
如何实现继承
- 首先要确定谁是父类,谁是子类
- 在定义类时,子类+(),()内写父类,实现继承
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class 父: pass
class 子(父): pass class Parent1: pass
class Parent2: pass
class Sub1(Parent1): pass
class Sub2(Parent1, Parent2): pass
print(Sub1.__bases__)
print(Sub2.__bases__)
|
注:一个子类可以继承多个父类,这是python独有的,其他语言只能一个子类继承一个父类
继承与抽象
如何寻找继承关系
想要寻找继承关系,首先要先抽象,再继承
抽象:抽象指的是抽取相似的地方
- 先抽象(抽象思想)
- 奥巴马 —-> 人类 —-> 动物类
- 猪坚强 —-> 猪类 —-> 动物类
- 阿黄 —-> 狗类 —-> 动物类
- 抽象定义动物类,为父类
- 再继承(程序中)
- 奥巴马对象 —-> 调用人类 —-> 继承动物类
- 猪坚强对象 —-> 调用猪类类 —-> 继承动物类
- 阿黄对象 —-> 调用狗类 —-> 继承动物类
继承的关系
- 对象是特征与技能的结合体
- 类是一系列相同对象的特征与技能的结合体
- 继承是一系列形同类的特征与技能的结合体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class People: school = 'oldboy'
def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex
class Teacher(People): def change_score(self): print(f'{self.name}老师正在该分数。。。')
class Student(People): def choose_course(self): print(f'{self.name}同学正在选课。。。')
tea1 = Teacher('tank', 18, 'male') stu1 = Student('小明', 18, 'male')
tea1.change_score() stu1.choose_course()
''' tank老师正在该分数。。。 小明同学正在选课。。。 '''
|
对象属性的查找顺序
- 对象查找属性会先从对象的名称空间中查找。
- 若对象没有,则从类中查找
- 若当前类是子类,并且没有对象找的属性,就去父类中查找
- 若父类中也没有机会报错
注:对象查找属性,若子类有,不管父类有没有,以子类的为准
验证查找顺序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Foo: def f1(self): print('Foo.f1')
def f2(self): print('Foo.f2') self.f1()
class Soo(Foo): def f1(self): print('Soo.f1')
soo_obj = Soo() soo_obj.f2() ''' Foo.f2 Soo.f1 '''
|
首先soo_obj
从对象自身找,没有去子类Soo中找,没找到,再去父类Foo中找,找到了打印’Foo.f2’,接着就是self.f1()
,self就是对象本身,按照对象—-> 子类—->父类的顺序找,再子类Soo中找到了打印’Soo.f1’
派生
派生指的是子类继承父类的属性,并且派生出新的属性。
子类派生出新的属性,若与父类的属性相同,则以子类的为准。
继承是谁与数的关系,指的是类与类的关系,子类与父类是从属关系
派生方法一(类调用)
直接通过父类.__init__
,把__init__
当作普通函数使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class People: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex
class Teacher(People): def __init__(self, name, age, sex, level, salary): People.__init__(self, name, age, sex) self.level = level self.salary = salary
class Student(People): def __init__(self, name, age, sex, course): People.__init__(self, name, age, sex) self.course = course
def choose_course(self): print(f'{self.name} is choosing {self.course}')
tea1 = Teacher('tank', 18, 'male', 9, '3.0') stu1 = Student('小明', 18, 'male', 'python') stu1.choose_course()
''' 小明 is choosing python '''
|
派生方法二(super)
- super是一个特殊的类,在子类中调用super()会得到一个特殊的对象,通过”.”指向父类的名称空间
super().__init__
(不用为self传值)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| class People: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex
class Teacher(People): def __init__(self, name, age, sex, level, salary): super().__init__(name, age, sex) self.level = level self.salary = salary
class Student(People): def __init__(self, name, age, sex, course): super().__init__(name, age, sex) self.course = course
def choose_course(self): print(f'{self.name} is choosing {self.course}')
tea1 = Teacher('tank', 18, 'male', 9, '3.0') stu1 = Student('小明', 18, 'male', 'python') stu1.choose_course()
|
新式类与经典类
- 在python2中,才会有新式类与经典类之分
- 在python3中,所有的类都是新式类
新式类
- 继承object的类都是新式类
- python3中,子类不继承自定义的类,默认继承object
经典类
在python2中,凡是没有继承object的类都是经典类
mro查看继承顺序
mro(): 是python内置的函数,用来查看当前类的继承顺序,在多继承的情况下
1 2 3 4 5 6 7 8 9 10 11 12
| class A: pass
class B: pass
class C(A, B): pass
print(C.mro())
|
钻石继承(菱形继承)
在多继承的情况下形成钻石继承
修改json数据格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| import json from datetime import datetime from datetime import date
dic = { 'name': 'tank', 'today1': date.today(), 'today2': datetime.today() }
class MyJson(json.JSONEncoder): def default(self, o): if isinstance(o, datetime): return o.strftime('%Y-%m-%d %X') elif isinstance(o, date): return o.strftime('%Y-%m-%d %X') else: return super().default(self, o)
res = json.dumps(dic, cls=MyJson) print(res) ''' {"name": "tank", "today1": "2019-10-10 00:00:00", "today2": "2019-10-10 17:00:20"} '''
|
组合
什么是组合
组合指的是一个对象中的属性,是另一个对象
为什么要使用组合
减少代码冗余
如何使用组合
继承实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
class People: def __init__(self, name, age, sex, year, month, day): self.name = name self.age = age self.sex = sex self.year = year self.month = month self.day = day
def tell_birth(self): print(f''' === 出生年月日 === 年: {self.year} 月: {self.month} 日: {self.day} ''')
class Teacher(People): def __init__(self, name, age, sex, year, month, day): super().__init__(name, age, sex, year, month, day)
class Student(People): def __init__(self, name, age, sex, year, month, day): super().__init__(name, age, sex, year, month, day)
tea1 = Teacher('tank', 18, 'male', 2001, 1, 1) tea1.tell_birth()
|
组合实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| class People: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex
class Teacher(People): def __init__(self, name, age, sex): super().__init__(name, age, sex)
class Student(People): def __init__(self, name, age, sex): super().__init__(name, age, sex)
class Date: def __init__(self, year, month, day): self.year = year self.month = month self.day = day
def tell_birth(self): print(f''' === 出生年月日 === 年: {self.year} 月: {self.month} 日: {self.day} ''')
tea1 = Teacher('tank', 18, 'male') date_obj = Date(2001, 1, 1)
tea1.date = date_obj tea1.date.tell_birth()
|
总结:
- 继承是类与类的关系,一种什么是什么的关系,子类与父类是一种从属关系
- 组合是对象与对象的关系,一种什么有什么的关系,一个对象拥有另一个对象
组合练习
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| ''' 选课系统需求: 1.学生类,老师类, 学生和老师都有课程属性, 每一门课程都是一个对象. 课程: 课程名字,课程周期,课程价钱
2.学生和老师都有选择课程的功能, 还有打印所有课程的功能. '''
class People: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex
def add_course(self, course_obj): self.course_list.append(course_obj)
def tell_all_course(self): for course_obj in self.course_list: course_obj.tell_course_info()
class Teacher(People): def __init__(self, name, age, sex): super().__init__(name, age, sex) self.course_list = []
class Student(People): def __init__(self, name, age, sex): super().__init__(name, age, sex) self.course_list = []
class Course: def __init__(self, course_name, course_period, course_price): self.course_name = course_name self.course_period = course_period self.course_price = course_price
def tell_course_info(self): print(f''' 课程名称:{self.course_name} 课程周期:{self.course_period} 课程价格:{self.course_price} ''')
tea1 = Teacher('tank', 18, 'male') stu1 = Student('小明', 18, 'male')
python_obj = Course('python', 6, 2) linux_obj = Course('linux', 6, 1)
tea1.add_course(python_obj) tea1.add_course(linux_obj)
tea1.tell_all_course()
|
封装
什么是封装
封装指的是把一堆属性封装到一个对象中
存数据的目的是为了取,对象可以”.”的方式,获取属性
为什么要封装
封装的目的是为了方便存取,可以通过对象.属性的方式获取属性
如何封装
特征:变量 —-> 数据属性
技能:函数 —-> 方法属性
在类内部,定义一堆属性(特征与技能)
访问限制机制
什么是访问限制机制
在类内部定义,凡是以__
开头的数据属性与方法属性,都会被python内部隐藏起来,让外部不能直接访问内部的__
开头的属性,如:__name = ‘小in’
访问限制机制的目的
一堆隐私的属性与不能被外部轻易访问的属性,可以隐藏起来,不能被外部直接调用
好处:对重要数据获取的逻辑更加严谨,进而保护了数据的安全
隐私属性可以通过封装一个接口,在接口内做业务逻辑的处理,再把数据返回给调用者
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class Foo: __name = 'tank'
def __run(self): print('running...')
def get_name(self): return self.__name
def set_name(self): self.__name = 'cwz'
foo = Foo()
foo.set_name() print(foo.get_name())
print(foo._Foo__name)
|
注意:在python中,不会强制限制属性的访问,类内部__开头的属性,只是做了一种变形。若想直接访问,调用变形后的名字即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| class Teacher: def __init__(self, name, age, sex): self.__name = name self.__age = age self.__sex = sex
def get_info(self): user = input('user:').strip() pwd = input('pwd:').strip() if user == 'tank' and pwd == '123':
print(f''' 姓名:{self.__name} 年龄:{self.__age} 性别:{self.__sex} ''')
def set_info(self, name, age, sex): if not isinstance(name, str): raise TypeError('名字必须要使用字符串')
if not isinstance(age, int): raise TypeError('年龄必须要使用数字')
if not isinstance(sex, str): raise TypeError('性别必须要使用字符串')
self.__name = name self.__age = age self.__sex = sex
t1 = Teacher('tank', 18, 'male') t1.get_info() class ATM: def __insert_card(self): print('开始插卡')
def __input_pwd(self): print('开始输入密码')
def __input_money(self): print('输入取款金额')
def __get_money(self): print('开始吐钱')
def __print_flow(self): print('打印账单')
def withdraw(self): self.__insert_card() self.__input_pwd() self.__input_money() self.__get_money() self.__print_flow() print('程序执行完毕')
atm = ATM() atm.withdraw()
|
property
什么是property
python内置的装饰器,主要是给类内部的方法使用
为什么要用property
使用它的目的,是将类内部的方法(def 方法名() 变成了(def 方法))
在对象使用某个方法时,将对象.方法()变成了对象.方法
如何使用property
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ''' 计算人的bmi:bmi值 = 体重 / (身高 * 身高) '''
class People: def __init__(self, name, weight, height): self.name = name self.weight = weight self.height = height
@property def bmi(self): return self.weight / (self.height ** 2)
p = People('cwz', 180, 1.8) print(p.bmi)
|
注意:不能对被装饰过的方法属性修改
但是也可以修改(了解)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class People: def __init__(self, name, weight, height): self.name = name self.weight = weight self.height = height
@property def bmi(self): return self.weight / (self.height ** 2)
@property def get_name(self): return self.name
@get_name.setter def set_name(self, val): self.name = val
@get_name.deleter def del_name(self): del self.name
p = People('cwz', 180, 1.8)
p.set_name = 'nick'
del p.del_name
|
多态
什么是多态
多态指的是同一种事物的多种形态
多态的目的
多态也称之为多态性,在程序中继承就是多态的表现形式
多态的目的是为了,让多种不同类型的对象,在使用相同功能的情况下,调用同一个名字的方法名
父类:定义一套统一的标准
子类:遵循父类统一的标准
多态的最终目的:统一子类编写的规范,为了让使用者更方便调用相同方法的功能
如何实现:
利用继承
抽象类
在python中,不会强制要求子类必须遵循父类的一套标准,所以出现了抽象类
abc模块
会强制子类遵循父类的一套标准
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import abc
class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod def eat(self): pass
@abc.abstractmethod def drink(self): pass
@abc.abstractmethod def speak(self): pass
class Pig(Animal): def eat(self): pass
def drink(self): pass
def jiao(self): print('哼哼哼。。。')
pig = Pig() pig.jiao()
|
鸭子类型
在不知道当前对象是什么的情况下,但你长得像鸭子,那么你就是鸭子类型
在python中,不推荐使用抽象类强制子类的定义,但是推荐子类都遵循鸭子类型
- 继承:耦合性太高,程序的可扩展性差
- 鸭子类型:耦合度第,程序的可扩展性强
多态炫技操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| class Animal: def eat(self): pass
def drink(self): pass
def speak(self): pass
class Pig(Animal): def eat(self): pass
def drink(self): pass
def speak(self): print('哼哼哼。。。')
class Cat(Animal): def eat(self): pass
def drink(self): pass
def speak(self): print('喵喵喵。。。')
class Dog(Animal): def eat(self): pass
def drink(self): pass
def speak(self): print('汪汪汪。。。')
pig = Pig() dog = Dog() cat = Cat()
def BARK(animal): animal.speak()
BARK(dog) BARK(pig) BARK(cat)
|
类的绑定方法和非绑定方法
classmethod
classmethod是一个装饰器,可以装饰给类内部的方法,使该方法绑定给类来使用
- 对象绑定方法特殊之处:由对象来调用,会把对象当作第一个参数传给该方法
- 类绑定方法特殊之处:由类来调用,会把类当作第一个参数传给该方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class People: def __init__(self, name, age): self.name = name self.age = age
@classmethod def tell_info(cls): print(cls) print('此处是类方法。。。')
People.tell_info() ''' <class '__main__.People'> 此处是类方法。。。 '''
|
小练习:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| ''' USER = 'tank' PWD = '123' '''
import settings
class Teacher: def __init__(self, user, pwd): self.user = user self.pwd = pwd
def index(self): if self.user == 'tank' and self.pwd == '123': print('验证成功,显示主页')
@classmethod def login_auth(cls): obj = cls(settings.USER, settings.PWD) return obj
obj = Teacher.login_auth() obj.index()
|
staticmethod
staticmethod是一个装饰器,可以装饰给类内部的方法,使得该方法既不绑定类,也不绑定对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import settings import uuid import hashlib
class Teacher: def __init__(self, user, pwd): self.user = user self.pwd = pwd
def index(self): if self.user == 'tank' and self.pwd == '123': print('验证成功,显示主页')
@classmethod def login_auth(cls): obj = cls(settings.USER, settings.PWD) return obj
@staticmethod def create_id(): uuid_obj = uuid.uuid4() md5 = hashlib.md5() md5.update(str(uuid_obj).encode('utf-8')) return md5.hexdigest()
print(Teacher.create_id()) t1 = Teacher('tank', 123) print(t1.create_id())
|
isinstance和issubclass
instance
python内置函数,可以传入两个参数,用于判断参数1是否是参数2的一个实例。
判断一个对象是否是一个类的实例
1 2 3 4 5 6 7 8 9
| class Foo: pass
foo_obj = Foo() print(foo_obj) print(foo_obj.__class__.__dict__) print(foo_obj in Foo.__dict__)
print(isinstance(foo_obj, Foo))
|
issubclass
python内置函数,可以传入两个参数,用于判断参数1是否是参数2的一个子类
判断一个类是否是一个类的子类
1 2 3 4 5 6 7 8 9
| class Foo: pass
class Goo(Foo): pass
print(issubclass(Goo, Foo))
|
反射
指的是通过“字符串”对 对象或类的属性进行操作
hasatter
通过字符串,判断字符串是否是对象或类的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class People: country = 'China'
def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex
p = People('tank', 18, 'male')
print('name' in p.__dict__)
print(hasattr(p, 'name')) print(hasattr(p, 'country'))
|
getatter
通过字符串,获取对象或类的属性4
1 2 3 4
| print(p.__dict__.get('name'))
print(getattr(p, 'name', 'jason'))
|
setatter
通过字符串,设置对象或类的属性
1 2
| setattr(p, 'level', '3.0') print(hasattr(p, 'level'))
|
delatter
通过字符串,删除对象或类的属性
1 2 3
| delattr(p, 'name')
print(hasattr(p, 'name'))
|
反射小练习
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Movie: def input_cmd(self): print('请输入命令......') while True: cmd = input('请输入执行的命令:').strip()
if hasattr(self, cmd): method = getattr(self, cmd) method()
def upload(self): print('正在上传...')
def download(self): print('正在下载...')
obj = Movie() obj.input_cmd()
|
魔法方法
凡是类内部定义,以__
开头,__
结尾的方法都称之为魔法方法,又称类的内置方法
魔法方法会在某些条件成立的时候触发
__init__
:在掉用类时触发
__str__
:在打印对象时触发
__del__
:会在程序结束时触发,销毁对象, 该方法会在最后执行
__getattr__
:会在对象.属性时,属性没有的情况下才会触发
__setattr__
:会在对象.属性 = 属性值的时候触发
__new__
:会在__init__
执行前触发
__call__
:会在对象调用时触发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| class Foo:
def __new__(cls, *args, **kwargs): print(cls) return object.__new__(cls)
def __init__(self): print('在调用类时触发...')
def __str__(self): print('在打印对象时触发') return '我是Foo实例化出的对象'
def __del__(self): print('对象被销毁前执行该程序')
def __getattr__(self, item): print('会在对象.属性时,属性没有的情况下才会触发') print(item)
def __setattr__(self, key, value): print('会在对象.属性 = 属性值的时候触发') print(key, value) print(self, 111) self.__dict__[key] = 1234
def __call__(self, *args, **kwargs): print(self) print('调用对象时触发')
obj = Foo()
|
魔法方法del应用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class MyFile(object): def __init__(self, file_name, mode='r', encoding='utf-8'): self.file_name = file_name self.mode = mode self.encoding = encoding
def file_open(self): self.f = open(self.file_name, self.mode, encoding=self.encoding)
def file_read(self): res = self.f.read() print(f''' 当前文件名称:{self.file_name} 当前文件内容:{res} ''')
def __del__(self): self.f.close() print('文件关闭成功')
f = MyFile('settings.py') f.file_open() f.file_read()
|
元类
创建类的两种方式
- 用关键字class创建类,就会自动调用type(),type会自动帮我创建一个类
1 2 3 4 5 6 7 8 9 10 11
| class People: def __init__(self, name, age): self.name = name self.age = age
def speak(self): print('speaking....') p = People('小明', 20) print(type(p)) print(type(People))
|
- 手动调用type(),实例化得到一个类
type() 括号里传三个参数:
- 参数1 类名
- 参数2 基类/父类
- 参数3 类的名称空间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class_name = 'People' class_base = (object,) class_dict = {}
code = ''' country = "China" def __init__(self, name, age): self.name = name self.age = age def speak(self): print('speaking...') ''' exec(code, {}, class_dict)
People = type(class_name, class_base, class_dict) print(People)
|
什么是元类
元类就是类的类,如上述例子中,class创建了一个People类,那People这个类是type的一个实例,type就是一个元类。
元类的作用
元类:就是控制类的创建
创建元类
- 自定义一个元类,继承type,派生出自己的属性和方法
- 给需要使用的类,通过metaclass指定自定义的元类
现在有两个需求:
- 要求创建类的类名必须首字母大写
- 要求创建类,必要在类中写注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class MyMeta(type): def __init__(self, class_name, class_base, class_dict): super().__init__(class_name, class_base, class_dict) print(class_name)
if not class_name.istitle(): raise TypeError('类名必须首字母大写')
if not class_dict.get('__doc__'): raise TypeError('类中要写注释')
def __call__(self, *args, **kwargs): obj = object.__new__(self) obj.__init__(*args, **kwargs) return obj
class Bar: pass
class Foo(Bar, metaclass=MyMeta): '''这是一个Foo类''' x = 10
def f1(self): print('from f1')
f = Foo()
|
单例模式
什么是单例
单例模式指的是单个实例,实例指的是调用类产生的对象
为什么要使用单例
实例化多个对象会产生不同的内存地址,单例可以调用者在调用同一个对象时,都指向同一个内存地址。例如打开文件。
单例的目的:为了减少内存的占用。
实现单例模式的几种方式
使用模块
其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc
文件,当第二次导入时,就会直接加载 .pyc
文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这样做:
1 2 3 4 5 6
|
class Singleton(object): def foo(self): pass singleton = Singleton()
|
将上面的代码保存在文件 mysingleton.py
中,要使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象
1
| from mysingleton import singleton
|
类内部定义静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| class File:
__instance = None
@classmethod def singleton(cls, file_name): if not cls.__instance: obj = cls(file_name) cls.__instance = obj return cls.__instance
def __init__(self, file_name, mode='r', encoding='utf-8'): self.file_name = file_name self.mode = mode self.encoding = encoding
def open(self): self.f = open(self.file_name, self.mode, encoding=self.encoding)
def read(self): res = self.f.read() return res
def close(self): self.f.close()
obj1 = File.singleton('settings.py') obj2 = File.singleton('settings.py') obj3 = File.singleton('settings.py') print(obj1) print(obj2) print(obj3) ''' <__main__.File object at 0x000001A04F472198> <__main__.File object at 0x000001A04F472198> <__main__.File object at 0x000001A04F472198> '''
|
装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| def Singleton(cls): _instance = {}
def _singleton(*args, **kargs): if cls not in _instance: _instance[cls] = cls(*args, **kargs) return _instance[cls]
return _singleton
@Singleton class A(object): a = 1
def __init__(self, x=0): self.x = x
a1 = A(2) a2 = A(3)
|
使用__new__
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| class File:
__instance = None
def __new__(cls, *args, **kwargs): if not cls.__instance: cls.__instance = object.__new__(cls) return cls.__instance
def __init__(self, file_name, mode='r', encoding='utf-8'): self.file_name = file_name self.mode = mode self.encoding = encoding
def open(self): self.f = open(self.file_name, self.mode, encoding=self.encoding)
def read(self): res = self.f.read() return res
def close(self): self.f.close()
obj1 = File('settings.py') obj2 = File('settings.py') obj3 = File('settings.py') print(obj1) print(obj2) print(obj3) ''' <__main__.File object at 0x00000184C9CD2198> <__main__.File object at 0x00000184C9CD2198> <__main__.File object at 0x00000184C9CD2198> '''
|
基于元类实现
1 2 3 4
| """ 1.类由type创建,创建类时,type的__init__方法自动执行,类() 执行type的 __call__方法(类的__new__方法,类的__init__方法) 2.对象由类创建,创建对象时,类的__init__方法自动执行,对象()执行类的 __call__ 方法 """
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import threading
class SingletonType(type): _instance_lock = threading.Lock() def __call__(cls, *args, **kwargs): if not hasattr(cls, "_instance"): with SingletonType._instance_lock: if not hasattr(cls, "_instance"): cls._instance = super(SingletonType,cls).__call__(*args, **kwargs) return cls._instance
class Foo(metaclass=SingletonType): def __init__(self,name): self.name = name
obj1 = Foo('name') obj2 = Foo('name') print(obj1,obj2)
|