第三章:Python高级编程-深入类和对象
第三章:Python高级编程-深入类和对象
3.1 鸭子类型和多态
"""
当看到一直鸟走起来像鸭子、游泳起来像鸭子、叫起来像鸭子,那么这只鸟就可以被称为鸭子。
这句话看上去有趣,却不太容易理解。接下来用实例来说明。
"""
# ============ Demo1 start =============
class Cat(object):
def say(self):
print("I am a cat")
class Dog(object):
def say(self):
print("I am a dog")
class Duck(object):
def say(self):
print("I am a duck")
animal = Cat
animal().say()
# ============ Demo1 end ===============
# ============ Java pseudocode contrast start =============
"""
在 Java中实现多态,需要子类继承父类并重写父类方法。并需要声明类型
"""
class Animal:
def say(self):
print("I am an animal")
class Dog(Animal):
def say(self):
print("I am an Doy")
Ainmal animal = Cat()
animal.say()
# ============ Java pseudocode contrast end =============
"""
在Python中就不一样了,如Demo1所示,变量animal可以指向任意类型,
所有类不需要继承父类,只需定义相同的方法say()就可以实现多态。再调用的时候,
只需调用共同say()方法。如下示例。
"""
# ============== Demo2 start ===================
class Cat(object):
def say(self):
print("I am a cat")
class Dog(object):
def say(self):
print("I am a dog")
class Duck(object):
def say(self):
print("I am a duck")
animal_list = [Cat, Dog, Duck]
for animal in animal_list:
animal().say()
# ============== Demo2 end ===================
"""
有内感觉了吗,反正我看到这,感触较深。嘎嘎嘎.....
老师又来了个例子。
"""
# ============== Demo3 start ====================
a = ["bobby1", "bobby2"]
name_tuple = ("bobby3", "bobby4")
name_set = set()
name_set.add("bobby5")
name_set.add("bobby6")
name_list = ["bobby7", "bobby8"]
a.extend(name_tuple)
a.extent(name_list)
a.extent(name_set)
# =============== Demo4 end ======================
"""
在 Demo3 中不知你是否发现除了列表本身,元组和集合对象都可以传入列表对象的
extend()方法。其实是extend()是接收一个可迭代对象,也就是前面章节所提到的
迭代类型,那么好玩的就来了。
"""
# =============== Demo5 start =====================
class Dog(object):
def say(self):
print("I am a dog")
def __getitem__(self):
print("loop!!!!!!!!")
a = ["bobby1", "bobby2"]
dog = Dog()
# name_tuple = ("bobby3", "bobby4")
# name_set = set()
# name_set.add("bobby5")
# name_set.add("bobby6")
# name_list = ["bobby7", "bobby8"]
# a.extend(name_tuple)
# a.extend(name_list)
a.extend(dog)
"""
结果:
loop!!!!!!!!
loop!!!!!!!!
loop!!!!!!!!
loop!!!!!!!!
loop!!!!!!!!
loop!!!!!!!!
loop!!!!!!!!
loop!!!!!!!!
loop!!!!!!!!
....
"""
# =============== Demo5 end =======================
"""
在 Demo5 中程序陷入了死循环,传入一个Dog对象也没有报错,
为什么?因为魔法函数,前面章节提到的__getitem__()是的对象
变成了可迭代对象,因此传入extend中,方法一直运行,知道抛出异常,
但是示例中是不会抛出异常的,因此会陷入死循环。
"""
3.2 抽象基类(abc模块)
"""
abc -> abstract base class
抽象基类相当于Java中的接口,Java无法实现多继承,
但可以继承多个接口,接口是不可以实例化的。所以说,
Python中的抽象基类也是不可以实例化的。Python是
动态语言,是没有变量类型的。实际上,变量只是一个符
号而已,它是可以指向任意类型的对象。动态语言不需要
指定类型,所以就少了一个编译时检查错误的环境,只有运
行时才知道错误。
与Java最大的一个区别就是,在定义一个类的时候,是不
需要去继承一个指定类型的。而要知道Python的一个类是
属于哪个类型的,是去看实现了那些魔法函数,魔法函数赋予
了类的一些特性。在实现了某个魔法函数之后,使得对象变成了
一个指定的类型,这种方法,在Python中可以说是一种协议。
在写代码是要尽量遵守这种协议,这样写出来的代码,才是
足够Python的一种代码。
"""
# ============ Demo1 start =============
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list
def __len__(self):
return len(self.employee_list)
com = Company(["bob", "jane"])
# 如何判断对象的类型呢?
# 第一种方案
print(hasattr(com, '__len__')) # 通过判断是否有某个属性而判断属于什么类型,不够直观
# 通过抽象基类
from collections.abc import Sized
print(isinstance(com, Sized)) # 这样的方式更加直观,易读
# ============ Demo2 end =============
"""
抽象基类的两个使用场景:
1. 我们在某些情况下希望判定某个对象的类型
2. 我们需要强制某个子类必须实现某些方法
"""
# =============== Demo2 start =================
# 如何去模拟一个抽象基类
class CacheBase():
def get(self, key):
raise NotImplementedError
def set(self, key, value):
raise NotImplementedError
class RedisCache(CacheBase):
pass
redis_cahe = RedisCache()
redis_cache.set("key", "value") # 会抛出异常,因为子类没有实现父类对应方法
# =============== Demo2 end ====================
"""
Demo2 的方法虽实现了第二个场景的需求,但是不够好,
只是在对象方法在调用是才抛出异常,如果想要在对象在
初始化就抛出异常,就需要使用我们的abc模块了。
"""
# ================= Demo3 start ===================
# 使用全局的abc模块
import abc
class CacheBase(metaclass=abc.ABCMeta):
@abc.abstractmethod
def get(self, key):
pass
@abc.abstractmethod
def set(self, key, value):
raise NotImplementedError
class RedisCache(CacheBase):
pass
redis_cache = RedisCache() # 抛出异常
# ================= Demo3 end ======================
3.3 使用instance而不是type
class A:
pass
class B(A):
pass
b = B()
print(isinstance(b, B)) # True
print(isinstance(b, A)) # True
print(type(b) is B) # is 判断是否是同一个对象
print(type(b) == B) # == 判断的是值是否相等
print(type(b) is A) # False
"""
注意isinstance比使用type好,type无法找到父类,
而isinstance可以。
同时注意 == 与 is 的区别。
"""
3.4 类变量和对象变量
# =========== Demo1 start ==============
class A:
aa = 1 # 类变量
def __init__(self, x, y):
self.x = x
self.y = y
a = A(2, 3)
print(a.x, a.y, a.aa) # 2 3 1
print(A.aa) # 1
print(A.x) # 抛出异常
# =========== Demo1 end ================
"""
在 Demo1 中打印a.aa时,首先会在对象属性中查找,
若是找不到则在类属性中查找。以上 Demo 很好理解。
"""
# ============ Demo2 start =================
class A:
aa = 1 # 类变量
def __init__(self, x, y):
self.x = x
self.y = y
a = A(2, 3)
A.aa = 11
print(a.x, a.y, a.aa) # 2 3 11
A.aa = 111
a.aa = 100
print(a.x, a.y, a.aa) # 2 3 100
# ============= Demo2 end ==================
"""
在对A.aa与a.aa同时赋值时,此时,对象属性中
就有了aa属性,所以在打印a.aa时,就会首先打
印对象里的属性啦。注意这个细节哦。类与实例的
变量是两个独立的存在。
"""
# 在Demo3中加入以下代码
b = A(3, 4)
print(b.aa) # 3 4 111
"""
可见类变量是所有实例共享的。
"""
3.5 类属性和实例属性以及查找顺序
"""
属性就是在类或实例中定义的变量或方法。
"""
class A:
name = "A"
def __init__(self):
self.name = "obj"
a = A()
print(a.name) # obj
"""
在单继承这很简单,但是在多继承下,这些就会变得复杂起来。
"""
MRO算法
Method Relation order
Python3使用的算法是C3,以下算法是Python早些版本的属性查找算法,均存在一些缺陷,下面一一介绍。
深度优先搜索

上图的继承关系中使用深度优先搜索是没有问题的,但是要是继承关系是菱形,如下图所示就会出现问题。需要使用广度优先搜索算法,使得继承顺序为A->B->C->D。
问题就是,下图中,如果C里的方法重写了D的方法。但是由于深度优先搜索算法会首先查找D中的属性,那么C的重写方法就不会生效。所有需要使用广度优先搜索算法解决问题。

广度优先
广度优先虽然解决了上述问题,但是呢,若果出现如下继承关系,广度优先算法又出现问题了。就是,如果D,C都有一个同名的方法,而继承D的B没有实现这个同名方法。那么在搜索完B时,应该搜索D,但是广度优先算法回去搜索C,这逻辑上是不合理的。

所以Python3统一成了一种方法,C3使得这些问题都不复存在。
# =============== 菱形继承问题 ==================
#新式类
class D:
pass
class B(D):
pass
class C(D):
pass
class A(B, C):
pass
print(A.__mro__)
"""
结果:
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)
"""
# ================ 非菱形继承问题 =================
class D:
pass
class B(D):
pass
class E:
pass
class C(E):
pass
class A(B, C):
pass
print(A.__mro__)
"""
结果:
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)
"""
3.6 静态方法、类方法以及对象方法以及参数
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def tomorrow(self):
self.day += 1
@staticmethod
def parse_from_string(data_str):
year, month, day = tuple(date_str.split("-"))
return Date(int(year), int(month), int(day)) # 出现硬编码情况
@classmethod
def from_string(cls, date_str):
year, month, day = tuple(date_str.split("-"))
return cls(int(year), int(month), int(day)) # 解决硬编码
def __str__(self):
return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)
if __name__ == "__main__":
new_day = Date(2020, 5, 7)
new_day.tomorrow()
print(new_day)
date_str = "2020-5-7"
new_day = Date.parse_from_string(date_str)
print(new_day)
3.7 数据封装和私有属性
class Date:
#构造函数
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def tomorrow(self):
self.day += 1
@staticmethod
def parse_from_string(date_str):
year, month, day = tuple(date_str.split("-"))
return Date(int(year), int(month), int(day))
@staticmethod
def valid_str(date_str):
year, month, day = tuple(date_str.split("-"))
if int(year)>0 and (int(month) >0 and int(month)<=12) and (int(day) >0 and int(day)<=31):
return True
else:
return False
@classmethod
def from_string(cls, date_str):
year, month, day = tuple(date_str.split("-"))
return cls(int(year), int(month), int(day))
def __str__(self):
return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)
class User:
def __init__(self, birthday):
self.__birthday = birthday # _User__birthday
def get_age(self):
return 2018 - self.__birthday.year
if __name__ == "__main__":
user = User(Date(1990, 2, 1))
print(user.birthday))
print(user.get_age())
3.8 Python对象自省机制
"""
自省就是通过一定的机制查询到对象的内部结构。
"""
class Person:
name = "User"
class Student(Person):
"""
文档
"""
def __init__(self, school_name):
self.school_name = school_name
if __name__ == "__main__":
stu = Student("家里蹲")
print(stu.__dict__) # {'school_name': '家里蹲'}
print(stu.name) # User
"""
stu的属性字典里没有name,那么是怎么能够得到User的呢?
实际上这个name在Person的属性字典里,类也是对象嘛!!
stu没有解释器就往上层找
"""
print(Person.__dict__) # {'__module__': '__main__', 'name': 'User', ...}
print(Student.__dict__) # {... '__doc__': '\n 文档\n ', ... }
print(dir(stu)) # ['__class__', '__delattr__', '__dict__', '__dir__', ...]
stu.__dict__["city"] = "WC"
print(stu.city) # WC
3.9 super函数
"""
super函数并没有那么简单...
"""
class A:
def __init__(self):
print("A")
class B(A):
def __init__(self):
print("B")
# super(B, self).__init__() # python2
super().__init__()
class C(A):
def __init__(self):
print("C")
super().__init__()
class D(B, C):
def __init__(self):
print("D")
super(D, self).__init__()
# 既然我们重写了B的构造函数,为什么还要去调用super?
"""
为了能够重用父类的一些方法,避免编写重复的逻辑
"""
# super到底执行顺序什么样?
"""
super并不是仅仅调用父类方法....
"""
if __name__ == "__main__":
d = D()
"""
直观结果:
D
B
A
"""
"""
实际结果:
D
B
C
A
"""
# 所以super的查找顺序是根据mro顺序来的
print(D.__mro__)
3.10 Django rest framework 中对多继承使用的经验
Mixin模式
- Mixin功能单一
- 不和基类关联,可以和任意基类组合,基类可以不和mixin关联就能初始化
- 在mixin中不要使用super这种用法
3.11 Python中的with语句
"""
try expect finally 的用法
"""
# ============== Demo1 start ====================
try:
print("code started")
raise KeyError
except KeyError as e:
print("key error")
else: # 没有异常再执行
print("other code")
finally:
print("finally") # 不管怎么样该行代码都会运行,用于关闭文件对象等
# ============== Demo1 end =====================
# ================== Demo2 start ========================
def exe_try():
try:
print("code start")
raise KeyError
return 1
except KeyError as e:
print("Key error")
return 2
else:
print("other error")
return 3
finally:
print("finally")
return 4
if __name__ == "__main__":
result = exe_try()
print(result)
"""
result 的结果会是什么呢?
答案是: 4
那么注释 return 4
结果又是什么呢?
答案是: 2
因为每次执行到return语句时,
其值都会压入栈中,最终去栈顶的值。
"""
# ================== Demo2 end ==========================
上下文管理协议
"""
基于:
__enter__(self)
__exit__(self, exc_type, exc_val, exc_tb)
"""
class Sample:
def __enter__(self):
# 获取资源
print("enter")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
# 释放资源
print("exit")
def do_something(self):
print("doing something")
with Sample() as sample:
sample.do_something()
"""
执行结果:
enter
doing something
exit
"""
3.12 contextlib实现上下文管理器
import contextlib
@contextlib.contextmanager
def file_open(file_name):
print("file open")
yield {}
print("file end")
with file_open("bobby.txt") as f_opened:
print("file processing")
"""
执行结果:
file open
file processing
file end
"""
第三章:Python高级编程-深入类和对象的更多相关文章
- 第十一章:Python高级编程-协程和异步IO
第十一章:Python高级编程-协程和异步IO Python3高级核心技术97讲 笔记 目录 第十一章:Python高级编程-协程和异步IO 11.1 并发.并行.同步.异步.阻塞.非阻塞 11.2 ...
- 简学Python第三章__函数式编程、递归、内置函数
#cnblogs_post_body h2 { background: linear-gradient(to bottom, #18c0ff 0%,#0c7eff 100%); color: #fff ...
- 第九章:Python高级编程-Python socket编程
第九章:Python高级编程-Python socket编程 Python3高级核心技术97讲 笔记 9.1 弄懂HTTP.Socket.TCP这几个概念 Socket为我们封装好了协议 9.2 cl ...
- Python高级编程-Python一切皆对象
Python高级编程-Python一切皆对象 Python3高级核心技术97讲 笔记 1. Python一切皆对象 1.1 函数和类也是对象,属于Python的一等公民 ""&qu ...
- Python高级编程和异步IO并发编程
第1章 课程简介介绍如何配置系统的开发环境以及如何加入github私人仓库获取最新源码. 1-1 导学 试看 1-2 开发环境配置 1-3 资源获取方式第2章 python中一切皆对象本章节首先对比静 ...
- python高级编程:有用的设计模式3
# -*- coding: utf-8 -*-__author__ = 'Administrator'#python高级编程:有用的设计模式#访问者:有助于将算法从数据结构中分离出来"&qu ...
- python高级编程:有用的设计模式2
# -*- coding: utf-8 -*- __author__ = 'Administrator' #python高级编程:有用的设计模式 #代理 """ 代理对一 ...
- python高级编程:有用的设计模式1
# -*- coding: utf-8 -*-__author__ = 'Administrator'#python高级编程:有用的设计模式#设计械是可复用的,某种程序上它对软件设计中觉问题提供的语言 ...
- python高级编程技巧
由python高级编程处学习 http://blog.sina.com.cn/s/blog_a89e19440101fb28.html Python列表解析语法[]和生成 器()语法类似 [expr ...
随机推荐
- floyd三重循环最外层为什么一定是K
Floyd算法为什么把k放在最外层? - 知乎 https://www.zhihu.com/question/30955032高票答案: 简单地总结一下:K没放在最外面一定是错的,但是在某些数据比较水 ...
- Spring3.2 中 Bean 定义之基于 XML 配置方式的源码解析
Spring3.2 中 Bean 定义之基于 XML 配置方式的源码解析 本文简要介绍了基于 Spring 的 web project 的启动流程,详细分析了 Spring 框架将开发人员基于 XML ...
- AJ学IOS(26)UI之iOS抽屉效果小Demo
AJ分享,必须精品 先看效果 实现过程 第一步,把三个view设置好,还有颜色 #warning 第一步 - (void)addChildView { // left UIView *leftView ...
- 怎么快速学python?酒店女服务员一周内学会Python,一年后成为程序员
怎么快速学python?有人说,太难!但这个女生却在一个星期内入门Python,一个月掌握python所有的基础知识点. 说出来你应该不信,刚大学毕业的女生:琳,一边在酒店打工,一边自学python, ...
- linux通过进程名查看其占用端口
1.先查看进程pid ps -ef | grep 进程名 2.通过pid查看占用端口 netstat -nap | grep 进程pid 参考: https://blog.csdn.net/sinat ...
- PHP本地开发利器:内置Web Server
PHP 5.4.0起, CLI SAPI 提供了一个内置的Web服务器. 命令:php -S 这个内置的Web服务器主要用于本地开发使用,不可用于线上产品环境. URI请求会被发送到PHP所在的的工作 ...
- Spring5:Java Config
@Configuration @Bean @ComponentScan @ImportResource 使用Java的方式配置spring,完全不使用spring配置文件,交给java来做! 两个注解 ...
- Ansible Facts 变量详解
Ansible Facts 变量详解与使用案例 主机规划 添加用户账号 说明: 1. 运维人员使用的登录账号: 2. 所有的业务都放在 /app/ 下「yun用户的家目录」,避免业务数据乱放: 3. ...
- Kubernetes笔记(一):十分钟部署一套K8s环境
Kubernetes是Goole开源的一个容器编排引擎,它支持自动化部署.大规模可伸缩.应用容器化管理 -- 百度百科. 接触K8s也有半年多了,也基于阿里云平台搭建了包含多级服务.目前运行较为稳定的 ...
- 解决Lost connection to MySQL server during query错误方法/Mysql关闭严格模式
使用Navicat 导入MySQL数据库的时候,出现了一个严重的错误,Lost connection to MySQL server during query,字面意思就是在查询过程中丢失连接到MyS ...