一、面向对象简介
1. 编程思想
- 总体上的编程思想有2种,一个是面向对象的编程思想,一个是面向过程的编程思想。
面向对象编程:Object Oriented Programming,简称OOP,是一种程序设计思想。
实际上,能够使用面向对象编程思想实现的程序,也都能通过面向过程完成。只是看哪种思想更适合当前开发需求。
- 面向过程与面向对象区别
面向过程:根据业务逻辑从上到下写代码,符合一般的逻辑思维顺序。
面向对象:将数据与函数绑定到一起,进行封装。减少重复代码的重写过程。
2. 面向对象概念及术语
• 类(Class): 用来描述具有相同属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。其中的对象被称作类的**实例。
• 对象:也称实例。通过类定义的初始化方法,赋予具体的值,成为一个”有血有肉的实体”。
• 实例化:创建类的实例的过程或操作。类似于函数的调用,然后赋值给一个新的变量,就是一个实例化的过程。
• 实例变量:定义在实例中的变量,只作用于当前实例。
• 类变量:类变量是所有实例公有的变量。类变量定义在类中,但在方法体之外。
• 数据成员:类变量、实例变量、方法、类方法、静态方法和属性等的统称。
• 方法:类中定义的函数。
• 静态方法:不需要实例化就可以由类执行的方法
• 类方法:类方法是将类本身作为对象进行操作的方法。
• 方法重写:如果从父类继承的方法不能满足子类的需求,可以对父类的方法进行改写,这个过程也称override。
• 封装:将内部实现包裹起来,对外透明,提供api接口进行调用的机制
• 继承:即一个派生类(derived class)继承父类(base class)的变量和方法。
• 多态:**根据对象类型的不同以不同的方式进行处理。
1. 类与对象介绍
- 类
类是抽象的概念,仅仅是模板。用来描述具有相同属性和方法的对象的集合。比如:”人”是一个类。
- 对象
某一个具体事物的存在 ,在现实世界中可以是看得见摸得着的。 比如:”胡歌”就是一个对象。
类与对象的关系,我们可以进行对象归类。比如:分析班级同学公有特征归类。
点击查看【processon】
2. 类的构成
类由3个部分构成
• 类的名称:类名
• 类的属性:一组数据
• 类的方法:允许对类进行操作的方法
注意:类名通常采用驼峰式命名方式,尽量让字面意思体现出类的作用。
3. 类的表达式:Python使用class关键字来定义类,其基本结构如下:
class 类名:
pass
3. 创建对象
python中,可以根据已经定义的类去创建出一个个对象,创建对象的格式为 :
对象名 = 类名() #这又是一个实例化过程,对象名也叫做实例
# 定义
class 类名:
def 方法名(self,参数): # 类中函数:称为方法
pass
# 执行
s = 类名() # 创建对象(实例) 整个过程就是实例化
s.方法名(参数) # 调用类中方法
1. self参数
在类当中定义方法时,会发现系统帮我们自动创建了self参数,并且在调用对象的该方法时,也无需传入self参数。那这个self是什么?实际上,我们需要明确self的两个概念
• self本身是形参
• self就是对象本身
- 练习:定义类为学生类,创建对象李四,在类中定义方法打印李四信息
class Student:
def info(self):
print(ls.name,ls.age)
ls = Student()
ls.name = "李四"
ls.age = 18
ls.info()
#out
李四 18
class LogicStudent: #类名
def test(self): #方法名
print(self) #类的内部函数叫做方法
ls = LogicStudent() #ls叫做实例/对象,这个过程叫做实例化
ls.test() #通过 对象名称.方法名(参数) 进行调用
print(ls)
print('-'*100)
zs =LogicStudent() #当对象/实例是谁,self就是谁
zs.test()
print(zs)
#out
<__main__.LogicStudent object at 0x0000000001EAA208>
<__main__.LogicStudent object at 0x0000000001EAA208>
----------------------------------------------------------------------------------------------------
<__main__.LogicStudent object at 0x0000000001EAABE0>
<__main__.LogicStudent object at 0x0000000001EAABE0>
在这个过程当中,对象将自身信息在类的外部定义赋值,封装到了类的内部。
点击查看【processon】
当我再想创建一个对象,并且打印出对象信息时,发现如果这样做不灵活。
class LogicStudent:
def test(self):
print(ls.name,ls.age)
ls = LogicStudent()
ls.name = '李四'
ls.age = 18
ls.test()
print(ls)
print('-'*100)
zs =LogicStudent()
zs.name = '张三'
zs.age = 20
zs.test()
print(zs)
#out
李四 18
<__main__.LogicStudent object at 0x00000000021EA208>
----------------------------------------------------------------------------------------------------
李四 18
<__main__.LogicStudent object at 0x00000000021EAA58>
所以优化如下:
class LogicStudent:
def test(self):
print(self.name,self.age)
ls = LogicStudent()
ls.name = '李四'
ls.age = 20
ls.test()
print(ls)
print('-'*100)
zs =LogicStudent()
zs.name = '张三'
zs.age = 20
zs.test()
print(zs)
#out
李四 20
<__main__.LogicStudent object at 0x00000000026AA208>
----------------------------------------------------------------------------------------------------
张三 20
<__main__.LogicStudent object at 0x00000000026AAA58>
但是这样仍然有缺陷,大家会发现,用户信息暴露在类的外部。
2. init()方法
init()方法称为初始化方法,也可称为构造方法。在创建对象时,会自动执行该方法,为对象的属性设置初始值。
init()方法不能自定义返回值
init()方法中声明的属性是允许在类的外部进行访问的
class Student():
def __init__(self,name,age):
self.name = name
self.age = age
def test(self):
print(self.name,self.age)
self.gender = 'female'
t = Student('张三',18) #实例化类,实际上t-->self
t.test()
print(t.name) #既然t-->self,那么t.name -->self.name,从而实现了访问
print(t.age)
print(t.gender)
#out
张三 18
张三
18
female
class Student():
def __init__(self):
print('1')
t = Student()
print('2')
#out
1
2
上题优化代码如下:
class Student:
def __init__(self,name,age): #形参接收
self.name = name
self.age = age
def test(self):
print(self.name,self.age)
ls = Student("张三",18) #实参传入
ls.test()
#out
张三 18
3. str()方法
如果在开发中,希望打印输出对象变量时,能够打印自定义的内容。就可以使用str()方法,将自定义内容通过return关键字返回。
class Student:
def __init__(self,name,age):
self.name=name
self.age=age
def __str__(self):
return f'{self.name},{str(self.age)}'
wei =Student('wei',18)
print(wei)
wang =Student('wangwu',22)
print(wang)
print(id(wei))
print(id(wang))
print(id(Student))
#out
wei,18
wangwu,22
38819264
38819320
37563656
- 注意:返回值必须是字符串。
4. 私有属性与私有方法
1. 私有属性定义
- 私有属性:就是对象不希望公开的属性
- 定义方式:在属性名前面增加两个下划线(例如:__name)
- 练习
• 定义类为:”人”类 • 创建对象:rose • 初始化对象属性:name与age • 要求:age不能在类的外部访问
class Person:
def __init__(self):
self.name = 'rose'
self.__age = 18
a = Person()
print (a.name)
print (a.age)
#out
AttributeError: 'Person' object has no attribute 'age'
rose
2. 私有属性访问
那如果有需求一定要在类的外部访问到对象的私有属性时,我们可以使用如下步骤:
- 可以通过类的内部的方法访问私有属性
- 通过类的内部方法将私有属性返回出去
- 在类的外部调用该方法并且接收就可访问
class Person:
def __init__(self):
self.name = 'rose'
self.__age = 18
def test(self):
return self.__age
a = Person()
print (a.name)
a_age = a.test()
print(a_age)
#out
rose
18
3. 私有方法定义
- 私有方法:就是对象不希望公开的方法
定义方式:在方法名前面增加两个下划线(例如:__test),代码如下:
class Test:
def tes1(self):
print("--1--")
def __tes2(self):
print("--2--")
def tes3(self):
print("--3--")
[x] 注意
在Python中,并没有真正意义上的私有,只有伪私有。当我们在访问时,以对象.类名_私有属性名称即可访问,私有方法同理。代码如下:
class Test:
def __init__(self,name,age):
self.name=name
self.__age=age
wei=Test('wei',22)
print(wei.name)
print(wei._Test__age)
print(id(wei))
print(id(wei.name))
print(id(wei._Test__age))
#out
wei
22
35476256
35453056
505704576
同样也需要注意的是,在实际开发中,不要通过该方式访问对象的私有属性或者私有方法。
- 玩个特别无聊的游戏,猜数字。 玩家输入一个数字与 计算机随机生成的数字作对比,当两个值相等时,则说明用户猜对了。注意:外部不能获取到计算机随机生成的值。
```python
import random
class GuessNum:
def init(self):
def test(self):self.__num = random.randint(1,6)
t=GuessNum() t.test()test_num = int(input("请输入:"))
while True:
if self.__num == test_num:
print("恭喜你 猜对了")
break
else:
test_num = int(input("你猜错了,请重新输入:"))
out
请输入:1 你猜错了,请重新输入:2 你猜错了,请重新输入:3 你猜错了,请重新输入:4 你猜错了,请重新输入:5 你猜错了,请重新输入:6 恭喜你 猜对了
- [x] 《寻欢作乐》《面纱》《月亮与六便士》《刀锋》...这些书籍的作者都是毛姆(以最节约内存的方式),并且管理员想统计每本书籍阅读的时长。使用面向对象的方式输出毛姆:《书籍名》总共阅读:**小时
```python
import time
class BookMan:
book_authr='毛姆'
def __init__(self,name):
self.name=name
self.read_time=0
def reading_time(self,num):
self.read_time+=num
def print_info(self):
print('{}:{},总计阅读时长为:{}小时'.format(BookMan.book_authr,self.name,self.read_time))
@classmethod
def set_authr(cls):
cls.book_authr='魏先生'
print(cls.book_authr)
@staticmethod
def get_systime():
print(time.strftime("%H:%M:%S",time.localtime()))
book1 = BookMan("《刀锋》")
book2 = BookMan("《月亮与六便士》")
book3 = BookMan("《寻欢作乐》")
book4 = BookMan("《面纱》")
book1.reading_time(2)
book1.reading_time(2)
book2.reading_time(2)
book3.reading_time(2)
book4.reading_time(2)
book1.print_info()
book4.print_info()
BookMan.set_authr()
BookMan.get_systime()
#out
毛姆:《刀锋》,总计阅读时长为:4小时
毛姆:《面纱》,总计阅读时长为:2小时
魏先生
14:42:11
- 创建一个煎饼类,调用烹饪时长的方法累计煎饼状态:如果煎的时间在0-3之间则状态为生的;如果煎的时间在3-5之间,则状态为半生不熟的;如果煎的时间在5-8之间,则状态为全熟的;当时间超过 8 分钟,状态焦了。并且还可以给煎饼添加作料,比如大葱(hhh)、大蒜(hhh)、烤肠等等
class Pancake:
def __init__(self):
self.cookState='生的'
self.cookLevel=0
self.addState=[]
def __str__(self):
return '煎饼的状态{},烤的时间为{},添加的辅料为{}'.format(self.cookState,self.cookLevel,self.addState)
def cook(self,cooktime):
self.cookLevel +=cooktime
if self.cookLevel>=0 and self.cookLevel<3:
self.cookState='生的'
elif self.cookLevel>=3 and self.cookLevel<5:
self.cookState='半生不熟'
elif self.cookLevel>=5 and self.cookLevel<8:
self.cookState='全熟'
elif self.cookLevel >= 8:
self.cookState = '糊了'
def add_addState(self,food):
self.addState.append(food)
pancake = Pancake()
pancake.cook(1)
pancake.add_addState('洋葱')
#out
煎饼的状态生的,烤的时间为1,添加的辅料为['洋葱']
5. 销毁对象
对象什么时候销毁:没有被引用时就会自动销毁
import sys
a=1
class Demo:
pass
d=Demo()
dd = d
print(sys.getrefcount(d))
del dd
print(sys.getrefcount(d))
print(sys.getrefcount(a))
#out
3
2
132
二、成员
1. 成员简介
类的成员可分为字段、方法以及属性。
2. 字段
1. 定义
上节课我们学习到的属性,更准确来说被称为字段。字段又主要可以分为普通(实例)字段/实例属性与静态(类)字段/类属性。
2. 普通字段
- 普通字段属于对象,保存在对象中,只能通过对象访问。
- 定义:self.字段名称
- 访问:self.字段名称 或 对象.字段名称
- 练习:定义一个省份类,打印输出班级每位同学所在的国家与省份。
class Province:
def __init__(self,country,name):
self.country=country
self.name=name
def print_info(self):
print(self.country,self.name)
wei = Province('中国','安徽')
wei.print_info()
#out
中国 安徽
但是这样实现,会有相应的问题。比如说,普通字段保存在对象中,也就是说,当创建一个对象时,就会重新创建self.country以及self.name字段。当存对象的字段值相同时,仍然使用普通字段便会造成我们内存的浪费。
3. 静态字段(类属性)
- 静态字段属于类,保存在类中。在创建时,仅创建一份,并且所有对象都共享静态字段。执行时可以是类访问也可以是对象访问。
- 定义:直接在类中定义
- 访问:类名.字段名称 或 self.字段名称 或 对象.字段名称
- 练习优化:
class Province:
country = '中国'
def __init__(self,name):
self.name=name
def print_info(self):
print(self.name,Province.country)
wei = Province('安徽')
wei.print_info()
#out
安徽 中国
总结
在实际开发中,如果字段的值是固定的,不随对象的改变而改变。那就使用静态字段(类属性),这样有利于节省内存的消耗。而如果字段的值随对象的改变而改变,就使用普通字段(实例属性)
3. 方法
1. 分类
类的方法主要可分为普通方法(实例方法),类方法,静态方法。
2. 普通方法(实例方法)
普通方法保存在类中,在实例化对象后,一般通过对象调用。第一个参数必须为系统自建参数,默认为self,代指对象本身。注意:self仅仅是变量名。使用其它也可,但最好不要修改。
定义及访问如下:
class Test:
def __init__(self):
self.name = 'rose'
def test(self):
print(self.name)
def test2(self):
self.test() #在类的内部的其它普通方法中,通过self调用
d.test() #在类的内部的其它普通方法中,通过对象调用
t = Test()
t.test()
t.test2()
应用场景:当方法中需要使用普通字段(实例对象)时,通常使用普通方法。
3. 静态方法
- 静态方法通过在方法上面添加@staticmethod装饰器定义,保存在类中;
- 静态方法不需要传入self参数,即使传入了self参数也并不像普通方法那样代指对象本身,仅仅是一个普通的形参。
- 静态方法的调用虽然可以通过对象调用,但一般由类直接调用。定义及访问如下:
```python
class Demo:
def init(self):
def test(self):self.name = "rose"
self.age = 18
@staticmethod # 通过@staicmethod装饰器将方法转为静态方法 def stat_test(): # 静态方法self不是必须加的,加了也不代表对象print(self.name)
rose = Demo() rose.test() Demo.stat_test() # 一般通过类名调用print("我是静态方法")
class Demo: def init(self): self.name=’rose’ self.age=18 def test(self): print(self.name) @staticmethod def stat_test(t): #这个参数相当于函数的形参 print(‘我是静态方法’) print(t) d=Demo() Demo.stat_test(‘111’) #将参数传入函数类的方法中,也相当于是静态方法的传参
- 应用场景
静态方法的参数并不代表对象这一特点,静态方法在类中使用起来更像一个单纯的函数。完全可以将静态方法放在类的外部,当成函数使用。但是放在类中,便于使用与维护。
- [ ] **练习:在类中定义一个打印输出当前时间的方法**
```python
import time
def show_time():
print(time.strftime("%H:%M:%S",time.localtime()))
show_time()
class Test:
@staticmethod
def show_time():
print(time.strftime("%H:%M:%S", time.localtime()))
t=Test()
Test.show_time()
4. 类方法
- 类方法通过在方法上面添加@classmethod装饰器,保存在类中;
- 类方法不需要传入self。但有一个系统自建参数为cls,cls代指类本身;
- 类方法一般通过类调用,也可通过对象调用。定义及访问如下
```python
class Demo:
@classmethod # 通过@classmethod装饰器将方法转为静态
def clasmd(cls): # 自建cls参数 代指 当前类
print(cls) # <class '__main__.Demo'>
d = Demo() Demo.clasmd() # 通过 类名 调用
<a name="3DzOz"></a>
#### 总结:
<br />一般而言<br />• 访问字段:对象.字段名称<br />• 访问方法:对象.方法名称([参数])
<a name="Ivnni"></a>
### 4. 属性
在实际开发中,为了简便,当想访问方法以属性的访问形式时,就可以通过在方法上面添加@property装饰器,达到该效果。
<a name="BR5Gn"></a>
#### 1. 定义及访问如下
```python
class Demo:
def __init__(self):
self.name = "rose"
@property
def test(self):
print(self.name)
d = Demo()
d.test # 通过@property 以 普通属性 形式 访问 普通方法
注意:以上@property只相当于一个只读属性,也就是说,仅能访问test函数的内容。
[x] 练习:需求1.通过传参去改变self.name的值;2.删除掉self.name。 ```python class Demo: def init(self):
self.name = "rose"
@property def test(self): # 只可访问模式
print(self.name)
@test.setter # d.set_test = “jack” def test(self,name):
self.name = name
print(self.name)
@test.deleter def test(self): # del d.test
del self.name
print(self.name) # 报错 self.name已被删除
d = Demo() d.test # 通过@property 只读模式
改变self.name的值
d.test = “jack” # 给方法传参 以 赋值方式 会触发@test.setter
删除self.name
del d.test # del 会 触发 @test.deleter
- [x] **练习:实现分页**
思路<br />• 用户输入查看的页面<br />• 比如输入1-->[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]<br />• 比如输入2-->[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]<br />• ...<br />面向过程代码
```python
li = [i for i in range(1000)] # 比如一共100页
while True:
p = int(input("请输入要查看的页码:"))
start = (p - 1) * 10
end = p * 10
print(li[start:end])
需求
• 使用面向对象编程思想实现
• 并且在类中定义start与end方法
• 通过调用start与end方法实现(尽量通过调用属性的方式实现)
class PageGame:
def __init__(self,page):
try:
page1 = int(page) # input默认为str,所以int强转
except Exception as e:
page1 = 1 # 万一有输入浮点..字母等会报错,多以捕捉异常,并且重新赋值为1
self.page = page1 # 将处理后的p赋值给实例属性page
@property
def start(self):
page = (self.page-1) * 10
return page
@property
def end(self):
page = self.page*10
return page
li = [i for i in range(1000)]
while True:
page = int(input("请输入要查看的页码:"))
test = PageGame(page)
print(li[test.start:test.end])
break
#out
请输入要查看的页码:2
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
- 类中有一个私有属性,__age。想要在外部可以对其访问,改变,删除 ,使用 property 的 fget,fset,fdel。
class Test(object):
def __init__(self):
self.__age=18
def get_age(self):
print(self.__age)
return self.__age
def set_age(self,age):
self.__age=age
print(self.__age)
def del_age(self):
print(self.__age)
del self.__age
print(self.__age)
age=property(fget=get_age,fset=set_age,fdel=del_age)
t=Test()
t.age
t.age='20'
del t.age
三、面向对象特征
1. 封装
1. 封装简介:
封装是面向对象编程的一大特点,将属性和方法放到类的内部,通过对象访问属性或者方法,隐藏功能的实现细节,也可以设置访问权限。
点击查看【processon】
2. 封装代码演练:
class Student:
def __init__(self,name,age):
self.name=name
self.age=age
def print_info(self):
print(self.name,self.age)
w = Student('wei',18)
w.print_info()
#out
wei 18
2. 继承(重点)
1. 继承介绍
继承是一种创建新类的方式,如果子类需要用到父类的属性或者方法时,就可以使用继承。当然,子类也可以提供自己的属性和方法。
2. 继承格式
class Father: #父类 或者超类、基类
pass
class Sun(Father): #子类 或者派生类
pass
注意:在Python中,新建的类可以继承一个或多个父类,也就是说可以单继承,也可以多继承
3. 继承的作用
避免重复造轮子的行为,减少冗余代码
4. 新式类和经典类
在Python2当中类分为新式类和经典类,如果有继承父类object则是新式类,否则为经典类。
但是在Python3当中,全部都是新式类,默认继承object。
[x] 练习:使用代码演练验证py3中的新式类
class Father: #py2中叫做经典类;py3中默认继承object,都为新式类
pass
class Son(object): #py2中叫做新式类
pass
f=Father()
s=Son()
print(len(f.__dir__()))
print(len(s.__dir__()))
#out
26 #26个方法
26 #26个方法
5. 单继承
子类继承父类,则可以直接享受父类中已经封装好的方法
[ ] 练习:用代码演练实现单继承的方法
class GrandFather(object):
def sleep(self):
print('GrandFather sleep')
class Father(GrandFather):
def eat(self):
print('Father eat')
def drink(self):
print('Father drink')
class Son(Father):
def study(self):
print('Son study')
s=Son()
s.eat()
s.study()
s.sleep()
#out
Father eat
Son study
GrandFather sleep
小结:
当对象调用方法时,查找顺序先从自身类找,如果自身没找到,则去父类找,父类无,再到父类的父类找,直到object类,若还无,则报错。这也称为深度优先机制。
当子类与父类拥有同名称的方法时,子类对象调用该方法优先执行自身的方法。那么实际上就是子类的方法覆盖父类的方法,也称为重写。
点击查看【processon】
在实际的开发中,遵循开放封闭原则。我们并不会完全的重写父类的方法,而是希望同时实现父类的功能。这时,我们就需要调用父类的方法了,可以通过super()函数实现。
- super()函数
super()函数是用于调用父类(超类)的一个方法。
- 语法
super(type[, object-or-type])
• type—>类
• object-or-type — 类,一般是 self
[x] 练习:继以上练习实现,在Son的sleep方法当中,调用父类的sleep方法。
class GrandFather(object):
def sleep(self):
print('GrandFather sleep')
class Father(GrandFather):
def eat(self):
print('Father eat')
def drink(self):
print('Father drink')
def sleep(self):
print('Father sleep')
super().sleep()
class Son(Father):
def study(self):
print('Son study')
def sleep(self):
print('Son sleep')
super().sleep() #或者super(Son,self).sleep() 或者Father(self).sleep()
s=Son()
s.eat()
s.study()
s.sleep()
#out
Father eat
Son study
Son sleep
GrandFather sleep
小结
- 继承 深度优先 先从自己找 找不到则往父类找
- 重写(优先执行子类中的方法)
- self永远是执行该方法的调用者
- super(子类,self)调用父类中的方法(arg) 或者使用父类名.父类中的方法(self,arg),这里也是深度优先
- 注意
- init方法也会继承,同实例方法(普通方法)一致
- 私有属性以及私有方法没有被继承
class GrandFather(object):
def __init__(self):
print('我是GrandFather中的构造方法')
def sleep(self):
print('GrandFather sleep')
class Father(GrandFather):
def __init__(self):
print('我是Father中的构造方法')
def eat(self):
print('Father eat')
def drink(self):
print('Father drink')
def sleep(self):
print('Father sleep')
super().sleep()
class Son(Father):
def __init__(self):
print('我是Son中的构造方法')
def study(self):
print('Son study')
def sleep(self):
print('Son sleep')
super().sleep()
s=Son()
s.eat()
s.study()
s.sleep()
#out
我是Son中的构造方法
Father eat
Son study
Son sleep
Father sleep
GrandFather sleep
class Fa(object):
def __init__(self):
self.name='wei'
self.__age=20
def test(self):
print('Fa test')
def __test2(self):
print('Fa __test2')
def test3(self):
print(self.__age)
self.__test2()
class Son(Fa):
def B_test(self):
print(self.__age)
s=Son()
s.test()
s.test3()
#out
Fa test
20
Fa __test2
6. 多继承
所谓多继承,即子类有多个父类,并且具有它们的特征。遵循左边优先原则:
点击查看【processon】
代码演练如下:
class F(object):
def run(self):
print('F run')
class F1(object):
def run(self):
print('F1 run')
class S(F1,F): #在这个位置进行顺序执行
pass
s=S()
s.run()
#out
F1 run
class GF(object):
def run(self):
print('GF run')
class F(GF):
pass
class F1(object):
def run(self):
print('F1 run')
class S(F,F1):
pass
s=S()
s.run()
#out
GF run
class GF(object):
def run(self):
print('GF run')
class F(GF):
pass
class F1(GF):
def run(self):
print('F1 run')
class S(F,F1):
pass
s=S()
s.run()
#out
F1 run
小结:基于C3算法,设计成如下执行顺序
print(S.__mro__)
#out
(<class '__main__.S'>, <class '__main__.F'>, <class '__main__.F1'>, <class '__main__.GF'>,
<class 'object'>)
3. 多态
1. 概念
多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚”鸭子类型”
动态语言调用实例方法时不检查类型,只要方法存在,参数正确,就可以调用。这就是动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态。2. 多态演练
class Person(object):
def print_info(self):
print('打印一条信息。。。')
class Man(Person):
def print_info(self):
print('。。。另外一条自我介绍')
def print_info(obj):
obj.print_info()
wei=Person()
print_info(wei)
wang=Man()
print_info(wang)
#out
打印一条信息。。。
。。。另外一条自我介绍
4. 常用魔法方法
1. 魔法方法简介
在Python中,有一些内置好的特定方法,这些方法在进行特定的操作时会自动被调用,称为魔法方法。
魔法方法的命名总是被双下划线包围,比如名称。doc
doc用来查看类的说明文档
[ ] 练习,查看列表类的说明文档
print(list.__doc__)
#out
list() -> new empty list
list(iterable) -> new list initialized from iterable's items
查看自定义类的说明文档 ```python class Demo(object): “”” 我是用来检测 类的说明文档的 “”” pass d=Demo() print(d.doc)
out
我是用来检测 类的说明文档的
- **__module__**
__module__用来查看当前操作的类所在模块,一个.py文件其实就是一个模块
- [ ] 练习:创建A.py与B.py,在A.py中定义类Demo,在A.py中输出Demo.__module__,在B.py中导入A中的Demo类运行B.py
- **总结**
当执行模块是类所在模块时,执行结果为__main__。否则,执行结果是类所在模块的名称。
- **__class__**
__class__用来查看当前对象的类
- [ ] 练习
• 创建Demo类<br />• 创建Demo对象为d<br />• 查看Demo类的__class__<br />• 查看Demo对象d的__class__
```python
class Demo(object):
"""
我是用来检测 类的说明文档的
"""
pass
d=Demo()
print(Demo.__class__)
print(d.__class__)
#OUT
<class 'type'>
<class '__main__.Demo'>
- 总结
类也是对象,Demo是type类的对象,d是Demo类的对象。
- dict
dict用于获取类或者实例的属性字典
注意
• 普通字段存储在对象中,所以通过对象.dict获取的是普通字段
• 除普通字段以外的成员都存储在类中,所以通过类.dict来获取
class Students(object):
def __init__(self):
self.name='wei'
self.__age=18
def test(self):
self.gender='male'
print('test')
wei=Students()
print(wei.__dict__)
#out
{'name': 'wei', '_Students__age': 18}
print(Students.__dict__)
#OUT
{'__module__': '__main__', '__init__': <function Students.__init__ at 0x00000000028A1F28>
, 'test': <function Students.test at 0x00000000028AA2F0>, '__dict__': <attribute '__dic
t__' of 'Students' objects>, '__weakref__': <attribute '__weakref__' of 'Students' obje
cts>, '__doc__': None}
- del()方法
del()方法也叫做析构方法。当由该类创建的实例对象,被删除或者说在内存中被释放,将会自动触发执行。
总结
• 当代码全部执行完毕才自动触发del()
• 如果需要提前触发,则需要通过del关键字,删除所有对象后触发del()
class Demo(object):
def __del__(self):
print('我被回收了')
d=Demo()
print('--'*50)
print('--'*50)
#out
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
我被回收了
class Demo(object):
def __del__(self):
print('我被回收了')
d=Demo()
print('--'*50)
del d
print('--'*50)
#out
----------------------------------------------------------------------------------------------------
我被回收了
----------------------------------------------------------------------------------------------------
注意
此方法一般不需要定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配与释放,一般都是交给Python解释器来执行。所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行。
- call()方法
call()方法用于将对象变成一个可调用的对象。也就是说,当一个类中有call()方法时,其实例化得到的对象便是可调用的(callable)
class Demo(object):
def __call__(self, *args, **kwargs):
print('可被调用了')
pass
d=Demo()
print(d)
d()
#out
<__main__.Demo object at 0x0000000001E9F9E8>
可被调用了
- new()方法
new()方法用于创建与返回一个对象。在类准备将自身实例化时调用
- 练习:以下代码打印输出的顺序?
A.init,new
B.init
C.new
D.new,init__
class Demo(object):
def __init__(self):
print('__init__')
def __new__(cls, *args, **kwargs):
print('__new__')
d=Demo()
#out
__new__
注意
• new()方法用于创建对象
• init()方法在对象创建的时候,自动调用
• 但是此处重写了父类的new()方法,覆盖了父类new()创建对象的功能,所以对象并没有创建成功。所以仅执行new()方法内部代码
- 对象创建执行顺序
• 1.通过new()方法创建对象
• 2.并将对象返回,传给init()
- 练习:在自定义类中实现创建对象
思路
• 重写父类new()方法
• 并且在该方法内部,调用父类的new()方法
class Demo(object):
def __init__(self):
print('__init__')
def __new__(cls, *args, **kwargs):
print('__new__')
return super().__new__(cls)
d=Demo()
#out
__new__
__init__
注意
• 在创建对象时,一定要将对象返回,才会自动触发init()方法
• init()方法当中的self,实际上就是new返回的实例,也就是该对象
init()与new()区别
• init实例方法,new静态方法
• init在对象创建后自动调用,new创建对象的方法
2.单例模式
思路
• 当对象不存在时,创建对象
• 当对象存在时,永远返回当前已经创建对象