PYTHON语言,一切皆对象!
究竟何谓对象?不同的编程语言以不同的方式定义“对象”。某些语言中,它意味着所有对象必须有属性和方法;另一些语言中,它意味着所有的对象都可以子类化。
在Python中,定义是松散的,某些对象既没有属性也没有方法,而且不是所有的对象都可以子类化。
1 但是Python的万物皆对象从感性上可以解释为:Python 中的一切都可以赋值给变量或者作为参数传递给函数。
Python 的所有对象都有三个特性:
- 身份:每个对象都有一个唯一的身份标识自己,任何对象的身份都可以使用内建函数 id() 来得到,可以简单的认为这个值是该对象的内存地址。
>>> a = 1
>>> id(a)
>>> 26188904 # 身份由这样一串类似的数字表示
- 类型:对象的类型决定了对象可以保存什么类型的值,有哪些属性和方法,可以进行哪些操作,遵循怎样的规则。可以使用内建函数 type() 来查看对象的类型。
>>> type(a)
<type 'int'>
>>> type(type)
<type 'type'> #万物皆对象,type 也是一种特殊的对象 type
- 值:对象所表示的数据
>>> a
“身份”、"类型"和"值"在所有对象创建时被赋值。如果对象支持更新操作,则它的值是可变的,否则为只读(数字、字符串、元组等均不可变)。只要对象还存在,这三个特性就一直存在。
对象的属性 :大部分 Python 对象有属性、值或方法,使用句点(.)标记法来访问属性。最常见的属性是函数和方法,一些 Python 对象也有数据属性,如:类、模块、文件等
2 Python 解释型语言
所谓编译执行就是源代码经过编译器编译处理,生成目标机器码,就是机器能直接运行的二进制代码,下次运行时无需重新编译。不过它是针对特定CPU体系的,这些目标代码只能在特定平台执行,如果这个程序需要在另外一种 CPU 上面运行,这个代码就必须重新编译。它不具备可移植性,但是执行速度快,C、C++这类语言属于编译型语言。
而解释型语言是在代码运行期间逐行翻译成目标机器码,下次执行时,还是需要逐行解释,我们可以简单认为 Java、Python 都是解释型语言。
-
编译型相当于厨师直接做好一桌子菜,顾客来了直接开吃,而解释型就像吃火锅,厨师把菜洗好,顾客需要自己动手边煮边吃。
-
效率上来说解释型语言自然比不过编译型语言,当然也不是绝对了,像 JIT 的效率就很高。
以上是对编译型语言和解释型语言的一个简单粗暴的区分,但是 Python(这里主要是指CPython)并不是严格的解释型语言;
因为 Python 代码在运行前,会先编译(翻译)成中间代码,每个 .py 文件将被换转成 .pyc 文件,.pyc 就是一种字节码文件,它是与平台无关的中间代码,不管你放在 Windows 还是 Linux 平台都可以执行,运行时将由虚拟机逐行把字节码翻译成目标代码。
我们安装Python 时候,会有一个 Python.exe 文件,它就是 Python 解释器,你写的每一行 Python 代码都是由它负责执行,解释器由一个编译器和一个虚拟机构成,编译器负责将源代码转换成字节码文件,而虚拟机负责执行字节码,所以,解释型语言其实也有编译过程,只不过这个编译过程并不是直接生成目标代码,而是中间代码(字节码),然后再通过虚拟机来逐行解释执行字节码。
总结
- Python代码首先会编译一个字节码文件,再由虚拟机逐行解释,把每一行字节码代码翻译成目标指令给CPU执行。
Python内部执行过程
- 一、编译过程概述
当我们执行Python代码的时候,在Python解释器用四个过程“拆解”我们的代码,最终被CPU执行返回给用户。
(1)首先当用户键入代码交给Python处理的时候会先进行词法分析,例如用户键入关键字或者当输入关键字有误时,都会被词法分析所触发,不正确的代码将不会被执行。
(2)下一步Python会进行语法分析,例如当"for i in test:"中,test后面的冒号如果被写为其他符号,代码依旧不会被执行。
(3)下面进入最关键的过程,在执行Python前,Python会生成.pyc文件,这个文件就是字节码,如果我们不小心修改了字节码,Python下次重新编译该程序时会和其上次生成的字节码文件进行比较,如果不匹配则会将被修改过的字节码文件进行覆盖,以确保每次编译后字节码的准确性。
那么什么是字节码?字节码在Python虚拟机程序里对应的是PyCodeObject对象。.pyc文件是字节码在磁盘上的表现形式。简单来说就是在编译代码的过程中,首先会将代码中的函数、类等对象分类处理,然后生成字节码文件。有了字节码文件,CPU可以直接识别字节码文件进行处理,接着Python就可执行了。
二、过程图解
二、过程图解
http://www.opython.com/1355.html
Python程序运行时,就像上面的例子一样,先将源代码完整的进行转换,编译成更有效率的字节码,保存成后缀为“.pyc”的字节码文件,然后,翻译器再通过这个文件一句一句的翻译为机器语言去执行。
3 Python 使用 LEGB 的顺序来查找一个符号对应的对象
locals -> enclosing function -> globals -> builtins
-
locals,当前所在命名空间(如函数、模块),函数的参数也属于命名空间内的变量
-
enclosing,外部嵌套函数的命名空间(闭包中常见)
def fun1(a):
def fun2():
# a 位于外部嵌套函数的命名空间
print(a)
- globals,全局变量,函数定义所在模块的命名空间
a = 1
def fun():
# 需要通过 global 指令来声明全局变量
global a
# 修改全局变量,而不是创建一个新的 local 变量
a = 2
- builtins,内置模块的命名空间。Python 在启动的时候会自动为我们载入很多内置的函数、类,比如 dict,list,type,print,这些都位于 builtins 模块中,可以使用 dir(builtins) 来查看。这也是为什么我们在没有 import 任何模块的情况下,就能使用这么多丰富的函数和功能了。
介绍完命名空间,就能理解 print(a) 这行代码输出的结果为什么是 a string 了。
零、类型
PYTHON 内置对象:
- PYTHON中,不需要事先声明变量名及其类型,直接赋值即可创建各种类型的变量。
- PYTHON属于强类型编程语言,使用变量时,需要程序员自己确定所进行的运算是否合适,以免出现异常或者意料之外的结果。
- PYTHON是一种动态类型语言,变量的类型可以随时变化。
对象类型 | 常量示例/用法 | 对象属性 |
---|---|---|
Number(数字) | 3.14159, 1234, 999L 3+4j | 不可变对象,修改变量值,不是真的修改变量内存地址的存储数值,而是把值放到内存中,然后修改变量,使其指向新的内存地址 |
String(字符串) | ‘spam’, “guido’s” | 不可变有序序列,使用单引号,双引号,三引号进行界定,并可互相嵌套,表示复杂的字符串,(1)采用%或则format(推荐)方式进行格式化,把其他类型的内容转换成需要的字符串,(2)采用转义字符表示对应的符号\n,\t,\r……,(3)采用r或者R表示原字符串 |
List(列表) | [1,[2, ‘Three’],4] | 可变有序序列 (1)是包含若干元素的有序连续内存空间,(2)当列表增加或者删除元素时,列表对象自动进行内存的扩展或收缩;(3)支持切片操作,不会因为下标越界而抛出异常,代码具有更强的健壮性。(4)切片操作可以实现原地修改列表内容,增加,删减,修改,查找,替换等操作,并且不影响列表对象的内存地址。(5)切片返回的是浅复制,与列表对象的直接赋值(引用)是不一样的。(6)支持列表推导式,用[ ]括起来的列表推导式,eg:alist=[x*x for x in range(10)] (7)深拷贝,需要用特定函数,实现深层(嵌套结构)的拷贝。 |
Dictionary(字典) | {‘food’:‘spam’, ‘taste’:‘yum’} | 可变无序序列(1)包含:键和值,(2) |
Set(集合) | {‘food’,‘taste’} | 可变无序序列 |
Tuple(元组) | (1,‘spam’,4,‘U’) | 不可变有序序列(1)一旦创建,不可以修改,增加和删除,(2)元组内如包含可变元素列表,可对该元素修改,(3)元组的处理速度比列表快,如只是遍历或者其他类似用途,建议采用元组,(4)扩展: 用( )括起来的生成器推导式,eg:atuple= (x*x for x in range(10)) 其结果为生成器对象! |
File(文件) | text=open(‘egges’,‘r’).read() |
C/C++数据类型:
C++ 为程序员提供了种类丰富的内置数据类型和用户自定义的数据类型。下表列出了七种基本的 C++ 数据类型:
类型 | 关键字 |
---|---|
布尔型 | bool |
字符型 | char |
整型 | int |
浮点型 | float |
双浮点型 | double |
无类型 | void |
宽字符型 | wchar_t |
一、基本语法
1、程序块语法方面:
c/c++中用一对“{}”将多段语句括起来,表示一个程序块,并以右大括号表明程序块结束
for(i=0;i<n;i++)
{
cout<<a[i];
j+=1;
}
Python中用缩进方式表明某几句语句为同一程序段
for i in n:
print(a)
j+=1
2、对for循环的使用
c/c++中用如下形式:for(i=0;i<n;i++){语句},主要是以某一可控值(如:n)控制循环的进行
Python中采用如下形式:for i in range:,采用序列的方式控制循环的进行
3、python的随机数产生
python的随机数产生函数random.randrange()如果传入两个参数,就会产生从低端点(包含)到高端点(不包含)之间的随机数
4、Python的不变性
python中的字符串中的元素不能通过直接赋值修改
如:
word="game"
word[0]="l"
这段代码就是错的
而在c/c++中则可以直接通过这种方式对字符串进行修改
需要理解的是,在python中对字符串变量重新赋值并不是改变了字符串,而是重新构造了字符串:test=“keyword”
这种不变性同样适用于元组(c/c++中的数组):test=(“keyword1”,“keyword2”,…)
如果需要对组合元素序列进行修改,可以使用列表或字典,这两种存储方式可以对其中的元素成员进行修改操作:test=[“keyword1”,“keyword2”,…]
5、python不需要对变量的类型进行申明,直接定义,且初始化只需要对变量赋空值即可,
如:cout=“”;
6、python可以用负值进行位置编号:
正值的编号是从左向右依次从0开始编号
负值的编号是从右向左一次从-1开始编号
7、python引入了字符串切片的概念:
采用a[start:finish]表示在字符串a中从编号start开始到编号finish之间的片段
切片概念同样适用于元组、列表和字典
8、Python中的列表和c/c++中的数组
Python中的列表可以利用切片的概念对列表进行整体赋值,如:inventory[4:6]=[“orb of future telling”],每个元素间只需空格隔开;
Python编译器自带数据访问的越界检查
Python的列表可以进行整体输入输出
python可以单个删除某个列表元素,也可以利用切片删除连续的多个元素
>>> test=["word","letter","honest","yes"]
>>> print(test)
['word', 'letter', 'honest', 'yes']
>>> del test[2]
>>> print(test)
['word', 'letter', 'yes']
>>>
c/c++中的数组任何情况下都不允许进行整体赋值(除非重新定义数组),且不会对数组进行越界检查
c/c++的数组不能整体输入输出,删除元素时也只能进行覆盖操作
9、Python中支持嵌套序列
如:可以再列表中包含其他列表或元组
对嵌套元素访问时,可以先把列表中的元组赋给另一个值,从另一个值访问被包含元组的成员值
>>> scores=[("a",100),("b",200)]
>>> score=scores[0]
>>> print(score)
('a', 100)
>>> print(score[0])
a
>>>
也可以直接用类似c/c++中二维数组的形式进行访问:先获得第一层序列的值,然后再获取该值第二层序列值(专业名词叫多重索引)
>>> score=[("a",100),("b",200)]
>>> print(score[0][0])
a
>>> print(score[0][1])
100
>>>
10、任何类型的序列都可以解包,分别把序列中的各个元素赋值给其他变量,这个是c/c++不具有的性质,使得Python有了更大的灵活性
***元组、列表、字典都是序列
>>> test=[("a",100),("b",400)]
>>> test1,test2=test
>>> print(test1)
('a', 100)
>>> print(test2)
('b', 400)
>>>
11、Python中对值的存储采用引用的方式,变量本身并不存储值,
如:test=“python”,只是在内存中存储这个字符串,用test作为名字指向这个字符串,对test的任何操作实际上都是对字符串本身的操作,这一点与c/c++不同,实例如下:
>>> test=["python","java"]
>>> print(test)
['python', 'java']
>>> test1=test
>>> print(test1)
['python', 'java']
>>> test1[0]="c/c++"
>>> print(test1)
['c/c++', 'java']
>>> print(test)
['c/c++', 'java']
>>>
以上例子说明,所谓的变量其实就是一个引用时使用的名字,对这个名字的任何操作,其实都是对他指向的实体内容的操作
但可以通过切片取得原来一个实体的副本,这样对副本的修改就不会影响到原来的实体,因为切片取得的永远都是一个副本
>>> test=["python","java"]
>>> print(test)
['python', 'java']
>>> test1=test[:]
>>> test1[0]="c/c++"
>>> print(test1)
['c/c++', 'java']
>>> print(test)
['python', 'java']
>>>
如以上结果,对test1的修改并没有影响到test所指的值,因为test1是从test切片过来的“副本”
12、python中的静态方法(方法前加入“@stasticmethod”修饰符)尽量不要和其他方法在命名上相同,因为python中在调用时,会优先调用静态方法,如果静态方法和其他方法名称相同,则在运行时会自动忽略对其他方法的调用
这一点上与c++的函数重载有所不同
13、C++中,外部函数对类私有成员的访问是绝对禁止的
但Python中对私有特性的访问却不是绝对禁止的,在私有特性前加入“—类名”,即可实现对其的访问,如:
同理,私有方法也是这样的
***同时,Python中提出了一种对特性进行控制的概念,即,对特定的特性修改时做部分限制,只需要在能访问该特性的方法前加入“@property”修饰符,将它设置为属性即可,
并且Python中可以为特性单独设置属性值,格式为:“@+属性的名称+“.”+setter”
14、python中可以直接在变量后携带两个字符串,用来创建新的字符串:
>>> foo="hello""world"
>>> foo
'helloworld'
15、python中引入了原始字符串操作符r/R,
用来把常用字符串按照字面意思来使用,而不考虑其转义特性:
>>> f=open("e:\readme.txt","r")
Traceback (most recent call last):
File "<pyshell#14>", line 1, in <module>
f=open("e:\readme.txt","r")
IOError: [Errno 22] invalid mode ('r') or filename: 'e:\readme.txt'
>>> f=open("e:\\readme.txt","r")
>>> f=open(r"e:\readme.txt","r")
以上的例子可以看到使用原始字符串操作符可以用直面语义使用字符串,相对原始的语法规定(包括c/c++)使用起来更为方便
16、python中,字符串不是以"NULL"或“\0”结束的,
在对字符串赋值时,不需要考虑字符串结束符的问题,这一点与c/c++有所不同,避免了c/c++由此造成的内存越界。
17、列表推导式
用[ ]括起来的列表推导式,eg:alist=[x*x for x in range(10)]
18、生成器推导式
用( )括起来的生成器推导式,eg:atuple= (x*x for x in range(10)) 其结果为生成器对象,使用next(),或则__next__()遍历!
19、yield语句
20、python3开头需要指定编码类型
# !/usr/bin/env python
# -*- coding: utf-8 -*-
21 全局变量,局部变量与nonlocal
- 全局变量即在整个程序中,任何地方都可以访问。
- 局部变量,试着在函数内部声明的变量,当函数运行结束时,局部变量就会被内存释放。
- 局部变量是有层次的,如果出现函数的嵌套,内层函数想调用外层的变量是,是不能用global声明的,因为外层的变量不叫全局变量。这时候如果想修改外层的变量,就得用到一个新的关键字:nonlocal
22 yield, 作用:(1)生成一个“生成器”对象,并不执行(2)采用next调用 ,才能执行(3)执行yield,作用相当于return,(4)程序停止,等待next调用,继续执行
22 数字 字符串 元祖 修改全局变量时需要加global,列表和字典以及其他可变类对象则不需要。
23 列表推导式,【表达式 for 变量 in 序列或者迭代对象】,相当于一个循环,生成的是一个列表。
24 生成器推导式, (表达式 for 变量 in 序列或者迭代对象), 生成的是一个生成器对象,只能通过next()方式访问。
二、 循环语句的区别
- 1 python没有do while语句
- 2 python没有三目运算符,用 语句:’ x if a==b else c’ 代替
三、字符串区别
待续
四、函数的区别
1 PYTHON不需要指定函数的类型
2 PYTHON函数采用return可以返回一个或者多个值,类似于MATLAB, return a1, a2
3 PYTHON函数不需要指定形参的类型,根据实际调用函数时传递的实参自动推断
4 在Python中参数传递采用的是值传递,这个和C语言有点类似;对于不可变对象,如:普通变量,。函数内部对形参进行修改,不会影响到实参的值;对于可变对象,如列表,字典,集合,对形参的修改将会影响实参的值。
5 PYTHON函数有关键参数,即按照参数名字来传值
6 PYTHON函数有可变长度参数,*parameter代表的是元组, **parameter代表的是字典
7 PYTHON具有lambda表达式,即用来声明匿名函数
8 作用域的LEGB原则
- L(Local)局部作用域
- E(Enclosing function locale)外部嵌套函数作用域
- G(Global module)函数定义所在模块作用域
- B(Buildin): python内置模块的作用域
9 eval()函数:把一个字符串当成一个表达式来执行, 返回表达式执行后的结果
10 exec()函数: 跟eval功能类似, 但是,不返回结果
11 高阶函数(函数名称就是一个变量): 把函数作为参数使用的函数,叫高阶函数
# 函数名称就是一个变量
def funA():
print("In funA")
funB = funA
funB()
12 系统高阶函数-map: 原意就是映射,即把集合或者列表的元素,每一个元素都按照一定规则(通常是函数)进行操作,生成一个新的列表或者集合
# map举例
# 有一个列表,想对列表里的每一个元素乘以10, 并得到新的列表
l1 = [i for i in range(10)]
print(l1)
l2 = []
for i in l1:
l2.append(i * 10)
print(l2)
# 利用map实现
def mulTen(n):
return n*10
l3 = map(mulTen, l1 )
# map类型是一个可迭代的结构,所以可以使用for遍历
for i in l3:
print(i)
# 以下列表生成式得到的结果为空, why?
l4 = [i for i in l3]
print(l4)
13 reduce函数: 原意是归并,缩减,把一个可迭代对象最后归并成一个结果,对于作为参数的函数要求: 必须由两个参数,必须由返回结果, reduce([1,2,3,4,5]) = f( f(f(f(1,2),3), 4),5),reduce 需要导入functools包
from functools import reduce
# 定义一个操作函数
# 加入操作函数只是相加
def myAdd(x,y):
return x + y
# 对于列表[1,2,3,4,5,6]执行myAdd的reduce操作
rst = reduce( myAdd, [1,2,3,4,5,6] )
print(rst)
14 filter 函数 过滤函数: 对一组数据进行过滤,符合条件(通常是函数)的数据会生成一个新的列表并返回;跟map相比较:
- 相同:都对列表的每一个元素逐一进行操作
- 不同:
- map会生成一个跟原来数据想对应的新队列
- filter不一定,只要符合条件的才会进入新的数据集合
# filter案例
# 对于一个列表,对其进行过滤,偶数组成一个新列表
# 需要定义过滤函数
# 过滤函数要求有输入,返回布尔值
def isEven(a):
return a % 2 == 0
l = [3,4,56,3,2,3,4556,67,4,4,3,23455,43]
rst = filter(isEven, l)
# 返回的filter内容是一个可迭代对象
print(type(rst))
print(rst)
print([i for i in rst])
15 闭包(closure)
- 当一个函数在内部定义函数,并且内部的函数应用外部函数的参数或者局部变量,
- 当内部函数被当做返回值的时候,相关参数和变量保存在返回的函数中,这种结果,叫闭包
- 上面定义的myF4是一个标准闭包结构
- 闭包关键特征:其变量为nonlocal类型
# 闭包常见坑
def count():
# 定义列表,列表里存放的是定义的函数
fs = []
for i in range(1,4):
# 定义了一个函数f
# f是一个闭包结构
def f():
return i*i
fs.append(f)
return fs
f1,f2,f3 = count()
print(f1())
print(f2())
print(f3())
输出结果:
9
9
9
出现的问题:
造成上述状况的原因是,返回函数引用了变量i, i并非立即执行,而是等到三个函数都返回的时候才统一使用,此时i已经变成了3,最终调用的时候,都返回的是 3*3
16 装饰器(Decrator)
- 在不改动函数代码的基础上无限制扩展函数功能的一种机制,本质上讲,装饰器是一个返回函数的高阶函数
- 装饰器的使用: 使用@语法, 即在每次要扩展到函数定义前使用@+函数名
- 装饰器的好处是,一点定义,则可以装饰任意函数
- 一旦被其装饰,则则把装饰器的功能直接添加到定义函数的功能上
# 任务:
# 对hello函数进行功能扩展,每次执行hello万打印当前时间
import time
# 高阶函数,以函数作为参数
def printTime(f):
def wrapper(*args, **kwargs):
print("Time: ", time.ctime())
return f(*args, **kwargs)
return wrapper
上面定义了装饰器,使用的时候需要用到@, 此符号是python的语法糖
# 上面定义了装饰器,使用的时候需要用到@, 此符号是python的语法糖
@printTime
def hello():
print("Hello world")
hello()
输出:
Time: Mon Apr 2 21:14:52 2018
Hello world
17 偏函数
- 参数固定的函数,相当于一个由特定参数的函数体
- functools.partial的作用是,把一个函数某些函数固定,返回一个新函数
import functools
#实现上面int16的功能
int16 = functools.partial(int, base=16)
int16("12345")
18 zip
- 把两个可迭代内容生成一个可迭代的tuple元素类型组成的内容
# zip 案例
l1 = [ 1,2,3,4,5]
l2 = [11,22,33,44,55]
z = zip(l1, l2)
print(type(z))
print(z)
for i in z:
print(i)
输出:
<class ‘zip’>
<zip object at 0x7f61c457ab88>
(1, 11)
(2, 22)
(3, 33)
(4, 44)
(5, 55)
19 enumerate
- 跟zip功能比较像
- 对可迭代对象里的每一元素,配上一个“索引”
- 然后索引和内容构成tuple类型
输出:# enumerate案例1 l1 = [11,22,33,44,55] em = enumerate(l1) l2 = [i for i in em] print(l2)
[(0, 11), (1, 22), (2, 33), (3, 44), (4, 55)]
五 对象的区别
0 python中类变量和成员变量、局部变量总结
class Member():
num=0 #类变量,可以直接用类调用,或用实例对象调用
def __init__(self,x,y):
self.x=x #实例变量(成员变量),需要它是在类的构造函数内以self.开头来定义的
self.y=y
self.fuc(self.x,self.y)
def add(self):
total=2 #局部变量
self.vara=3 # 局部变量,虽是以self.给出,但并没有在构造函数中进行初始化
self.varb=4
fina=(self.x+self.y)*total
return fina
def fuc(self,a,b):
self.varc=a #成员变量,他们在成员函数fuc()中定义,但是在构造函数中调用了fuc()函数
self.vard=b
- (1) 可变变量作为类变量:对于列表、字典、自定义类这些可变变量,如果将其作为类变量,则是传引用。即所有对象的类变量公用一个内存地址。
- (2)不可变变量作为类变量:对于INT,STRING这种不可变变量,如果将其作为类变量,则是传值。即所有对象的类变量有各自的内存地址。
- (3)不管是可变变量还是不可变变量,只要是放在构造函数中,则都是传值。即各个对象拥有自己的对象属性。
1 PYTHON类的成员函数第一个参数固定为self
2 PYTHON构造函数为__init__()
3 _ init_()内定义和类方法内定义的变量为对象的数据成员
4 类内定义的变量为类的数据成员
5 可以动态的为类和对象增加成员
6 类的保护成员和保护函数前面用“_+成员名(函数名)"
7 类的私有成员和私有函数前面用”__+成员名(函数名)"
8 类的方法有:公有方法,私有方法,静态方法,类方法
9 PYTHON 属性, 可设置为只读;可修改;可删除(针对可动态的为类和对象增加成员)
- 属性结合了公开数据成员和成员方法的优点,既可以像成员方法那样对值进行必要的检查(类似于C++的私有,公开,保护),又可以像数据成员一样灵活的访问。
10 数字 字符串 元祖 修改全局变量时需要加global,列表和字典以及其他可变类对象则不需要。
11 访问基类可以通过:“super(继承类名, self).方法名( ) ”或者“基类名.方法名( )”的方式
- super不是关键字, 而是一个类
- super的作用是获取MRO(MethodResolustionOrder)列表中的第一个类
- super于父类直接没任何实质性关系,但通过super可以调用到父类
- super使用两个方,参见在构造函数中调用父类的构造函数
12 多态就是同一个对象在不同情况下有不同的状态出现,Mixin设计模式
-
Mixin设计模式
- 主要采用多继承方式对类的功能进行扩展
- Mixin概念
- MRO and Mixin
- Mixin模式
- Mixin MRO
- MRO
-
我们使用多继承语法来实现Minxin
-
使用Mixin实现多继承的时候非常小心
- 首先他必须表示某一单一功能,而不是某个物品
- 职责必须单一,如果由多个功能,则写多个Mixin
- Mixin不能依赖于子类的实现
- 子类即使没有继承这个Mixin类, 也能照样工作,只是缺少了某个功能
-
优点
- 使用Mixin可以在不对类进行任何修改的情况下,扩充功能
- 可以方便的组织和维护不同功能组件的划分
- 可以根据需要任意调整功能类的组合
- 可以避免创建很多新的类,导致类的继承混乱
-
# Mixin案例
class Person():
name = "liuying"
age = 18
def eat(self):
print("EAT.......")
def drink(self):
print("DRINK......")
def sleep(self):
print("SLEEP.....")
class Teacher(Person):
def work(self):
print("Work")
class Student(Person):
def study(self):
print("Study")
class Tutor(Teacher, Student):
pass
t = Tutor()
print(Tutor.__mro__)
print(t.__dict__)
print(Tutor.__dict__)
print("*"*20)
class TeacherMixin():
def work(self):
print("Work")
class StudentMixin():
def study(self):
print("Study")
class TutorM(Person, TeacherMixin, StudentMixin):
pass
tt = TutorM()
print(TutorM.__mro__)
print(tt.__dict__)
print(TutorM.__dict__)
13 类的成员描述符是为了在类中对类的成员属性进行相关操作而创建的一种方式
- get: 获取属性的操作
- set:修改或者添加属性操作
- delete: 删除属性的操作
- 如果想使用类的成员描述符,大概有三种方法
- 使用类实现描述器
- 使用属性修饰符
- 使用property函数
- property函数很简单
- property(fget, fset, fdel, doc)
- 案例参看notebook
- 如果想使用类的成员描述符,大概有三种方法
# peroperty案例
# 定义一个Person类,具有name,age属性
# 对于任意输入的姓名,我们希望都用大写方式保存
# 年龄,我们希望内部统一用整数保存
# x = property(fget, fset, fdel, doc)
class Person():
'''
这是一个人,一个高尚的人,一个脱离了低级趣味的人
他还他妈的有属性
'''
# 函数的名称可以任意
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进行下下操作啦")
# 作业:
# 1. 在用户输入年龄的时候,可以输入整数,小数,浮点数
# 2. 但内部为了数据清洁,我们统一需要保存整数,直接舍去小数点
14 类的内置属性
- __ dict__:以字典的方式显示类的成员组成
- __ doc__: 获取类的文档信息
- __ name__:获取类的名称,如果在模块中使用,获取模块的名称
- __ bases__: 获取某个类的所有父类,以元组的方式显示
15 类的常用魔术方法
-
魔术方法就是不需要人为调用的方法,基本是在特定的时刻自动触发
-
魔术方法的统一的特征: 方法名被前后各两个下滑线包裹
-
操作类
- __ init__ : 构造函数
# init 举例
class A():
def __init__(self, name = 0):
print("哈哈,我被调用了")
a = A()
- __new__: 对象实例化方法,此函数较特殊,一般不需要使用
- __call__: 对象当函数使用的时候触发
# __call__举例
class A():
def __init__(self, name = 0):
print("哈哈,我被调用了")
def __call__(self):
print("我被调用了again")
a = A()
a()
- __ str__: 当对象被当做字符串使用的时候调用
# __str__举例
class A():
def __init__(self, name = 0):
print("哈哈,我被调用了")
def __call__(self):
print("我被调用了again")
def __str__(self):
return "图灵学院的例子"
a = A()
print(a)
- __ repr__: 返回字符串,跟__str__具体区别请百度
- 描述符相关
__ set__
__ get__
__ delete__ - 属性操作相关
__ getattr__: 访问一个不存在的属性时触发
# __getattr__
class A():
name = "NoName"
age = 18
def __getattr__(self, name):
print("没找到呀没找到")
print(name)
a = A()
print(a.name)
print(a.addr)
# 作业:
# 为什么会打印第四句话,而且第四句话是打印的 None
- __ setattr__: 对成员属性进行设置的时候触发
16 类和对象的三种方法
- 实例方法
- 需要实例化对象才能使用的方法,使用过程中可能需要截止对象的其他对象的方法完成
- 静态方法
- 不需要实例化,通过类直接访问
- 类方法
- 不需要实例化
17 抽象类
-
抽象方法: 没有具体实现内容的方法成为抽象方法
-
抽象方法的主要意义是规范了子类的行为和接口
-
抽象类的使用需要借助abc模块
import abc
- 抽象类:包含抽象方法的类叫抽象类,通常称为ABC类
18. 结构体
有时候我们可能需要像C中的struct那样的数据类型,把少量的数据项放在一起。Python中可以使用定义一个空类来实现这一点:
# filename:p.py
class Employee:
pass
john = Employee() # Create an empty employee record
填充数据:
# Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000
>>> import p
>>> p.john
<p.Employee instance at 0xb71f50ac>
>>> p.john.name
'John Doe'
>>> p.john.dept
'computer lab'
>>> p.john.salary
1000
六、文件操作
- 长久保存信息的一种数据信息集合
- 常用操作
- 打开关闭(文件一旦打开,需要关闭操作)
- 读写内容
- 查找
1 open函数
-
open函数负责打开文件,带有很多参数
-
第一个参数: 必须有,文件的路径和名称
-
mode:表明文件用什么方式打开
- r:以只读方式打开
- w:写方式打开,会覆盖以前的内容
- x:创建方式打开,如文件已经存在,报错
- a:append方式,以追加的方式对文件内容进行写入
- b: binary方式,二进制方式写入
- t: 文本方式打开
- +: 可读写
-
open(file, mode=‘r’, buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
- 注意:中文必须加上: encoding='UTF-8’
# 打开文件,用写的方式
# r表示后面字符串内容不需要转义
# f称之为文件句柄
f = open(r"test01.txt", 'w')
# 文件打开后必须关闭
f.close()
# 此案例说明,以写方式打开文件,默认是如果没有文件,则创建
注意:此案例说明,以写方式打开文件,默认是如果没有文件,则创建
2 with语句
- with语句使用的技术是一种成为上下文管理协议的技术(ContextManagementProtocal)
- 自动判断文件的 作用域, 自动关闭不在使用的打开的文件句柄
建议用with语句 - r"test01.txt"中的r表示原义
# with语句案例
with open(r"test01.txt", 'r') as f:
pass
# 下面语句块开始对文件f进行操作
# 在本模块中不需要在使用close关闭文件f
2.1 readline函数用来一行一行的读取文件
# with案例
with open(r'test01.txt', 'r', encoding = 'UTF-8') as f:
# 按行读取内容
strline = f.readline()
# 此结构保证能够完整读取文件知道结束
while strline:
print(strline)
strline = f.readline()
输出:
假若他日相逢
我将何以贺你
以沉默
以眼泪
2.2 list用打开的文件作为参数,把文件内每一行内容作为一个元素
# list能用打开的文件作为参数,把文件内每一行内容作为一个元素
with open(r'test01.txt', 'r', encoding = 'UTF-8') as f:
# 以打开的文件f作为参数,创建列表
l = list(f)
for line in l:
print(line)
输出:
假若他日相逢
我将何以贺你
以沉默
以眼泪
2.3 read是按字符读取文件内容
- read( )表示从文件头读到文件尾
- read(n)表示读n个字符,一个汉字包含3个字节
- read 可以与tell以及seek函数联合,获取任意地址的文件内容
# read是按字符读取文件内容
# 允许输入参数决定读取几个字符,
# 如果没有指定参数,从当前位置读取到结尾
# 否则,从当前位置读取指定个数字符
with open(r'test01.txt', 'r', encoding = 'UTF-8') as f:
strChar = f.read(1)
print(len(strChar))
print(strChar)
# 作业:
# 使用read读取文件,每次读取一个,使用循环读完
# 尽量保持格式
输出:
1
假
3 seek(offset, from)
- 移动文件的读取位置,也叫读取指针
- from的取值范围:
- 0: 从文件头开始偏移
- 1:从文件当前位置开始偏移
- 2: 从文件末尾开始偏移
- 移动的单位是字节(byte)
- 一个汉字由若干个字节构成
- 返回文件只针对 当前位置
# seek案例
# 打开文件后,从第5个字节出开始读取
# 打开读写指针在0处, 即文件的开头
with open(r'test01.txt', 'r', encoding = 'UTF-8') as f:
# seek移动单位是字节
f.seek(6, 0)
strChar = f.read()
print(strChar)
输出:
他日相逢
我将何以贺你
以沉默
以眼泪
4 UTF-8中的中文字符占用3个字节
- 查看每个字符的二进制输出
# 每个字符的二进制内容
with open(r'test01.txt', 'r', encoding = 'UTF-8') as f:
strChar = f.read(3)
for i in strChar:
print(type(i))
print(len(i))
j = bytes(i,'UTF-8')
for k in j:
print(bin(k))
输出:
<class ‘str’>
1
0b11101111
0b10111011
0b10111111
<class ‘str’>
1
0b1010
<class ‘str’>
1
0b11100101
0b10000001
0b10000111
4.1 tell函数: 用来显示文件读写指针的当前位置
- 用法:pos = f.tell( )
# tell函数: 用来显示文件读写指针的当前位置
with open(r'test01.txt', 'r', encoding = 'UTF-8') as f:
strChar = f.read(3)
pos = f.tell()
while strChar:
print(pos)
print(strChar)
strChar = f.read(3)
pos = f.tell()
# 以下结果说明:
# tell的返回数字的单位是byte
# read是以字符为单位
输出:
9
假若他
18
日相逢
25
我将
34
何以贺
41
你
以
48
沉默
57
以眼泪
5 文件的写操作-write
- write(str): 把字符串写入文件
- writeline(str): 把字符串按行写入文件
- 区别:
- write函数参数只能是字符串
- writerline参数可以是字符串,也可以是字符序列
# write 案例
# 1. 向文件追加一句诗
# a代表追加方式打开
with open(r'test01.txt', 'a', encoding = 'UTF-8') as f:
# 注意字符串内含有换行符
f.write("生活不仅有眼前的苟且, \n 还有远方的苟且")
# 可以直接写入行, 用writelines
# writelines表示写入很多行,参数可以是list格式
# a代表追加方式打开
with open(r'test01.txt', 'a') as f:
# 注意字符串内含有换行符
f.writelines("生活不仅有眼前的苟且")
f.writelines("还有远方的枸杞")
l = ["I", "love", "wangxiaojing"]
# a代表追加方式打开
with open(r'test01.txt', 'w') as f:
# 注意字符串内含有换行符
f.writelines(l)
6 持久化 - pickle
- 序列化(持久化,落地):把程序运行中的信息保存在磁盘上
- 反序列化: 序列号的逆过程
- pickle: python提供的序列化模块
- pickle.dump:序列化
- pickle.load:反序列化
# 序列化案例
import pickle
age = 19
with open(r'test01.txt', 'wb', encoding = 'UTF-8') as f:
pickle.dump(age, f)
# 反序列化案例
import pickle
with open(r'test01.txt', 'rb') as f:
age = pickle.load(f)
print(age)
输出:
19
# 序列化案例
import pickle
a = [19, 'liudana', "i love wangxiaojing", [185, 76]]
with open(r'test01.txt', 'wb') as f:
pickle.dump(a, f)
with open(r'test01.txt', 'rb') as f:
a = pickle.load(f)
print(a)
输出:
[19, ‘liudana’, ‘i love wangxiaojing’, [185, 76]]
7 持久化-shelve
- 持久化工具
- 类似字典,用kv对保存数据,存取方式跟字典也类似
- open, close
# 使用shelve创建文件并使用
import shelve
# 打开文件
# shv相当于一个字典
shv = shelve.open(r'shv.db')
shv['one'] = 1
shv['two'] = 2
shv['three'] = 3
shv.close()
# 通过以上案例发现,shelve自动创建的不仅仅是一个shv.db文件,还包括其他格式文件
- shelve打开了必须关闭
- 最好采用:**try…finally… **语句
# shelve读取案例
shv = shelve.open(r'shv.db')
try:
print(shv['one'])
print(shv['threee'])
except Exception as e:
print("烦死了")
finally:
shv.close()
输出:
1
烦死了
8 json特性
- JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。
- JSON的数据格式其实就是python里面的字典格式,里面可以包含方括号括起来的数组,也就是python里面的列表。
- Json 模块提供了四个方法: dumps、dump、loads、load
import json
x = [1,2,3]
y = json.dumps(x)
print(type(y))
json.loads(y)
输出:
<class ‘str’>
[1, 2, 3]
8.1 json的 dumps 和 dump:
- dumps和dump 序列化方法
- dumps只完成了序列化为str,
- dump必须传文件描述符,将序列化的str保存到文件中
def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None,
default=None, sort_keys=False, **kw):
# Serialize ``obj`` to a JSON formatted ``str``.
# 序列号 “obj” 数据类型 转换为 JSON格式的字符串
def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None,
default=None, sort_keys=False, **kw):
"""Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
``.write()``-supporting file-like object).
我理解为两个动作,一个动作是将”obj“转换为JSON格式的字符串,还有一个动作是将字符串写入到文件中,也就是说文件描述符fp是必须要的参数 """
实例:
>>> import json
>>> json.dumps([]) # dumps可以格式化所有的基本数据类型为字符串
'[]'
>>> json.dumps(1) # 数字
'1'
>>> json.dumps('1') # 字符串
'"1"'
>>> dict = {"name":"Tom", "age":23}
>>> json.dumps(dict) # 字典
'{"name": "Tom", "age": 23}'
a = {"name":"Tom", "age":23}
with open("test.json", "w", encoding='utf-8') as f:
# indent 超级好用,格式化保存字典,默认为None,小于0为零个空格
f.write(json.dumps(a, indent=4))
# json.dump(a,f,indent=4) # 和上面的效果一样
8.2 json的loads 和 load
- loads和load 反序列化方法
- loads 只完成了反序列化,
- load 只接收文件描述符,完成了读取文件和反序列化
def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw):
"""Deserialize ``s`` (a ``str`` instance containing a JSON document) to a Python object.
将包含str类型的JSON文档反序列化为一个python对象"""
def load(fp, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw):
"""Deserialize ``fp`` (a ``.read()``-supporting file-like object containing a JSON document) to a Python object.
将一个包含JSON格式数据的可读文件饭序列化为一个python对象"""
实例:
>>> json.loads('{"name":"Tom", "age":23}')
{'age': 23, 'name': 'Tom'}
import json
with open("test.json", "r", encoding='utf-8') as f:
aa = json.loads(f.read())
f.seek(0)
bb = json.load(f) # 与 json.loads(f.read())
print(aa)
print(bb)
输出:
{‘name’: ‘Tom’, ‘age’: 23}
{‘name’: ‘Tom’, ‘age’: 23}
总结
-
-
json序列化方法:
dumps:无文件操作 dump:序列化+写入文件
-
-
-
json反序列化方法:
loads:无文件操作 load: 读文件+反序列化
-
修改自图灵学院课程文件
9 CSV文件读写
csv(comma separeated values) 格式的文件常用于电子表格和数据库中的导入和导出。
- csv.reader(fp,delimiter= ’ ‘,quotechar=’ ')
- csv.writer(fp,delimiter=’ ‘, quotechar=’ ')
– writerrow( ) - csv.DictReader(fp)
- csv.DictWriter(fp,fieldnames=’ ')
七 异常处理
待续
八 多线程与进程
待续