1. Python
1.1 概述
Python常用于:
- 人工智能
- 数据分析
- 机器学习
- 爬虫
- 自动化测试
- 自动化运维
- web开发
- …
Python开发的框架:
Google开源机器学习框架:TensorFlow
开源社区主推学习框架:Scikit-learn
百度开源深度学习框架:Paddle
Python是一门解释型语言,因此使用python编程必须先安装python解释器
也因为python是解释型语言,是脚本语言,不是编译型语言,所以代码从上到下运行,是可以不需要主方法的
下面的语法讲解主要讲同java的区别以及python的一些特殊语法
2 基础语法
2.1 语法习惯
-
没有分号结尾 -
以缩进表示代码块间关系 所以如果一个if里面或者方法/类里面没东西时一定要写pass,否则报错 -
同MATLAB一样,如果一行中仅输入变量名或常量,将打印在控制台 -
注释 单行注释用# 多行注释用’’'或者""",如下:
'''
多行注释
'''
"""
俺也是多行注释
"""
- 使用pycharm进行编程时自带一个控制台终端, 相当于在命令行敲代码
2.2 输入输出函数
2.2.1 输入函数:input()
从python3开始input返回值类型一律为字符串 , 所以在输入数字时要使用int()函数(实际上是类,数据类型)进行转型 , 而java不同, Java转型没有int()函数, int是关键字, python的int是函数而不是关键字
Java转型:
String a = "3";
int b = Integer.parseInt(a);
double c = 3;
int d = (int) c;
python转型:
a = '3'
b = int(a)
在input括号里放的东西会被打印出来, 即input(“提示信息”), 可以将input输入的东西存储为变量:
a = input("请输入你的年龄:")
print(a)
print(type(a))
2.2.2 输出函数:print()
2.2.2.1 参数表
下面是print函数调用参数表
print(*objects, sep=’ ‘, end=’\n’, file=sys.stdout)
- objects – 复数,表示可以一次输出多个对象。输出多个对象时,需要用 , 分隔
- sep – 用来间隔多个对象,默认值是一个空格。
- end – 用来设定以什么结尾。默认值是换行符 \n,我们可以换成其他字符串。
- file – 要写入的文件对象。
- print()函数会自动换行
因为end默认为’\n’,想实现不换行,可以换成其他字符串""(如空) 示例:
E = [1, 2, 3]
for i in E:
print(i, end=" ")
# 现在打印结果如下
# 1 2 3
2.2.2.2 分隔符
print打印多个表达式时,用逗号分隔他们,结果是在参数中插入一个空格字符:
print('Age:' , 10)
但也可以像java一样通过"+"拼接,此时需要前后都是字符串,不会插入空格:
print('Age:'+10)
2.2.2.3 格式化输出:
占位符基本与java一致, 常用占位符:
和Java类似的占位符输出格式:
%06d,表示输出的整数显示位数,不?以0补全,超出当前位数则原样输出
%.2f,表示?数点后显示的?数位数
注意:
? 由于python的格式化输出都是将值转化为字符串输出, 因此不管输出什么都可以只有%s占位符
和Java有区别的地方:
Java格式化输出通过逗号:
int a = 1;
System.out.printf("a=%d", a);
python(不使用%分隔会失去格式化作用):
name = 'zhangsan'
age = 3
print('name=%s' % name)
print("abc%s" % "d")
print('name=%s age=%d' % (name,age))
格式化字符串除了%s,还可以写为 f'{表达式}' (从python3.6开始增加的格式化语法,这种语法更加简单易读):
name = 'zhangsan'
age = 3
print(f'我的名字是{name}, 明年{age + 1}岁了')
2.3 变量和数据类型
2.3.1 标识符
- 由字母,数字,下划线组成
- 不能以数字开头
- 区分大小写
- 以下划线开头的标识符是特殊意义的
- 单下划线开头代表不能直接访问的类属性,需要通过类提供的接口访问,不能用from xxx import *导入
- 双下划线开头表示类的私有成员
- 双下划线开头和结尾表示python特殊方法专用的标识,如:__xx__表示Python内置标识,__xx__()标识构造方法
2.3.2 数据类型
- int : 整数, 包括任意大小
- 前缀OB或者Ob,表示二进制
- 前缀OO或者Oo,表示八进制
- 前缀OX或者Ox,表示十六进制
- 关于复数,虚部只能用j表示
- float: 浮点数, 小数
- str : 字符串, 可以单引号’双引号"三引号’’'或者"""括起来的文字,几种表示无区别
- bool : 布尔, 只有True,False俩种值
- 空值: 用None表示
- list : 列表
- tuple : 元组
- set : 集合
- dict : 字典
可以使用type()函数检测数据类型
2.3.3 变量
变量就是?个存储数据的的时候当前数据所在的内存地址的名字?已, python是弱类型语言, 定义变量不需要指定数据类型:
a = 3
b = 'b'
同时可以使用变量来引用函数 例如: foo = math.sqrt,可以使用foo来计算平方根,如foo(4)
同时python支持链式赋值 可以将多个变量同时关联到同一个值 示例:
x = y = somefunction()
x = somefunction()
y = x
x = somefunction()
y = somefunction()
也支持同时给多个变量赋值:
x,y,z = 1,2,3
x,y = y,x
a,b,*c = [1,2,3,4]
a = "w s n b"
first,*mid,last = a.split()
2.4 转换数据类型的函数
前面将input函数时已经说过使用int()函数可以将数据类型转换为整型了, 类似的, python还有其他可以进行数据类型转换的函数:
函数 | 说明 |
---|
int(x [,base ]) | 将x转换为?个整数 | float(x ) | 将x转换为?个浮点数 | complex(real [,imag ]) | 创建?个复数,real为实部,imag为虚部 | str(x ) | 将对象 x 转换为字符串 | repr(x ) | 将对象 x 转换为表达式字符串 | eval(str) | ?来计算在字符串中的有效Python表达式,并返回?个对象 还可以将字符串中的数据转换成Python表达式原本类型 | tuple(s) | 将序列 s 转换为?个元组 | list(s ) | 将序列 s 转换为?个列表 | chr(x ) | 将?个整数转换为?个Unicode字符 | ord(x ) | 将?个字符转换为它的ASCII整数值 | hex(x ) | 将?个整数转换为?个?六进制字符串 | oct(x ) | 将?个整数转换为?个?进制字符串 | bin(x ) | 将?个整数转换为?个?进制字符串 |
其中eval()函数较为常用:
2.4.1 eval()
在Python中evel()函数的语法格式为eval(expression, globals=None, locals=None) ,注意后面还有globals参数和locals参数。eval()函数用于执行一个字符串表达式,并且返回该表达式的值。与eval相近的有exec函数
- expression:表达式,上面提到evel函数用于执行一个字符串表达式,表达式的内容就放在此处
- globals:该部分必须是字典!必须是字典!必须是字典!否则程序会出错。当定义了globals 参数之后eval函数的作用域会被限定在globals中, 表示全局命名空间(存放全局变量)
- locals:该参数掌控局部的命名空间,功能和globals类型,不过当参数冲突时,会执行locals处的参数, 表示当前局部命名空间(存放局部变量), 最优先拿数据的作用域
示例:
a=10;
print(eval("a+1"))
因为此处没有指定globals和locals,所以直接执行expression部分的内容。该程序的效果等价于a=10 print(a+1)
带globals:
a=10;
g={'a':4}
print(eval("a+1",g))
由于globals限制了作用域, 因此这里的a就只能是globals中的作用域, 因此打印5
而local是限制最大的作用域, 优先从local中拿数据, 再从globals中拿数据, 最后都没有才会去字典外作用域拿数据, 示例:
a=10
b=20
c=30
g={'a':6,'b':8}
t={'b':100,'c':10}
print(eval('a+b+c',g,t))
注意:
? eval函数非常的方便,我们可以使用一行代码就实现计算器的功能:
print(eval(input('请输入')))
? 但是因为它具有可以将字符串转成表达式执行的特性,所以它也就可以去执行系统命令。这样很容易被别有用心的人用来执行系统命令,删除关键系统文件, 因此eval函数必须慎用
除此之外eval函数还可以将字符串中的数据转换成Python表达式原本类型:
str1 = '10'
str2 = '[1, 2, 3]'
str3 = '(1000, 2000, 3000)'
print(type(eval(str1)))
print(type(eval(str2)))
print(type(eval(str3)))
2.4.2 exec函数
前面介绍了eval函数可以实现自动运行字符串中的计算表达式, exec函数类似, 但是exec函数能够动态地执行复杂的Python代码, 也就是说exec函数除了执行计算表达式字符串之外, Python代码字符串也是可以直接通过exec函数执行的
看下区别:
# 报错, 无法执行a=3这种Python代码
eval('a=3 ; print(a)')
# 输出3
exec('a=3 ; print(a)')
因此我们可以读取 Python代码的txt文件, 再通过exec函数运行
类似于eval函数, 同样有globals和locals参数
2.5 import语法
俩种模块导入:
-
import 模块名 只导入模块,不导入模块中具体对象,使用时要用模块名.对象名的方式进行访问 -
from 模块名 import 对象名1(函数名) 对象名2(函数名) … 只能导入模块中具体对象,使用时直接使用对象名不需要加上模块名(但是这种方法可能导致导入的对象或者函数与自己写的代码重名) 例如当导入了俩个具有相同名字函数的模块时, 执行的时候调?到的是后?导?的模块的功能 一次导入模块中所有对象:from 模块名 import * 相当于导入一个模块中的一个函数并为他创建一个对象, 是这个意思吗? -
俩种都可以在后面加上: as 别名
和Java区别:
? Java的import更多的是导入类或者静态变量
2.6 运算符
-
** : 幂运算 -
/ : 相除,结果为浮点数 -
// : 整除 -
% : 取余,当除数(后面那个)是负数时结果为负,其他为正 -
逻辑与: and 逻辑或: or 逻辑非: not 小知识:python中的布尔运算符在判断时只做必要的计算 如:x and y中,如果x为假直接返回x,x为真则返回y,而不关心y的具体(在python中除了少数值外其他几乎所有值都为真) 而在x or y中同样如此,x为真直接返回x,为假则返回y 以下代码就利用了这种行为: name = input('Please enter your name:') or 'unknown'
'''
如果没有输入名字,则可以直接将name设置为'unknown'
但在大多数情况下,你宁愿使用条件表达式,而不耍这样的短路花样
'''
-
按位与: &,如5&6,转换为二进制101 & 110 = 100 = 4,所以5&6=4 按位或: 5 | 6 = 101 | 110 = 111 = 7,所以5|6 = 7 按位异或: 5 ^ 6 = 101 ^ 110 = 011 = 3,所以5 ^ 6 = 3 tips: 异或是当符号前后俩者分别为T和F时才为T(所以运算针对二进制),俩个数字做异或时,先转换成二进制数,再把每一位的数字俩俩对比 -
>> << : 右移,左移 -
== != : 是否等于,是否不等 -
is,is not : 身份运算符 in,not in : 成员运算符 -
运算符优先级:
运算符 | 描述(由上至下由高到低) |
---|
** | 幂 | ~ + - | 取反,正号,负号 |
与Java主要区别:
2.6.1 is,is not与==,!=
is,is not对比的是俩个变量的内存地址,==,!=对比的是俩个变量的值.由此:
- 对于地址不可变的类型(str等),is,==是完全等价的(==时代表俩者相等,is需要俩者相同,即地址一致)
- 对于地址可变的类型(list(列表),dict(字典),tuple(元组),set(集合)等),俩者有区别,但python对字符串可以直接使用==,因为str是不可变的类型,这是与java不同的
a = "Hello"
b = "Hello"
print(a is b,a == b)
a = ["Hello"]
b = ["Hello"]
print(a is b,a == b)
和Java区别:
? Java是对于基本数据类型, ==判断值是否相等, 对于引用数据类型, ==判断地址, equals判断值(因为一般引用数据类型的equals是被重写过的)
2.6.2 三目运算符
语法:
条件成?执?的表达式 if 条件 else 条件不成?执?的表达式
示例:
a = 1
b = 2
c = a if a > b else b
print(c)
Java语法:
int c = 2>1 ? 2:1;
2.6.3 交换变量值
使用Java或者C的写法交换:
c = 0
c = a
a = b
b = c
print(a)
print(b)
用python自带的特点交换:
a, b = 1, 2
a, b = b, a
print(a)
print(b)
2.7 流程控制基本语句
2.7.1 if-elif-else
条件语句:if,elif,else.不需要括号,但需要在结尾加上冒号:
if 条件:
条件成?执?的代码1
条件成?执?的代码2
elif 条件2:
条件2成?执?的代码1
条件2成?执?的代码2
else:
都不成?执?的代码1
都不成?执?的代码2
示例:
if True:
print('条件成?执?的代码1')
print('条件成?执?的代码2')
print('我是?论条件是否成?都要执?的代码')
和Java区别:
? else if变成了elif, 多了个:少了个()
注意:
- 布尔表达式(如用作if语句中的条件)时,以下值都将被解释为假:
False None 0 “” () [] {}
- in可以用于条件表达式中
- 使用两个字符串中的字符执行Python字符串比较
两个字符串中的字符被一一比较 当找到不同的字符时,将比较它们的Unicode值。Unicode值较低的字符被认为较小 可以使用ord()函数来获取Unicode码 比较序列时同样将元素一一比较
2.7.2 while
while循环: 无括号,加冒号 :
while 条件:
条件成?重复执?的代码1
条件成?重复执?的代码2
......
示例:
i = 1
result = 0
while i <= 100:
result += i
i += 1
print(result)
python的循环也支持break,continue ,不管是for循环还是while循环, 效果都和Java一致
while-else:
? 可以在循环中加入else子句, 此时的else子句仅在break没有执行时执行 (即循环正常结束时)
示例:
i = 1
result = 0
while i <= 100:
result += i
i += 1
else:
result += 1
print(result)
i = 1
result = 0
while i <= 100:
result += i
i += 1
if i == 3:
break
else:
result += 1
print(result)
2.7.3 for循环
for…in循环:遍历任何序列(或任何可迭代对象) , 序列下面会讲:
常用到的可迭代的对象包括:string, list, dict, tuple, generator, range函数
for 临时变量 in 序列:
重复执?的代码1
重复执?的代码2
.....
示例:
str = "Python"
for s in str:
print(s)
for i in range(0,5):
print(i)
d = {'x':1,'y':2}
for key in d:
print(key,d[key])
for key value in d.items():
print(key,value)
for-else:
? 可以在循环中加入else子句(循环下面,缩进与for,while一个位置),此时的else子句仅在break没有执行时执行 (即循环正常结束时)
3. 常用模块与函数
3.0 Python的模块概述
python中的模块含义:
模块是包含了所定义的函数和变量的文件,必须以.py为拓展名
也就是说一个.py文件就是一个模块, 一个模块中可以包含若干函数和变量以及类等
在实际开中,当?个开发?员编写完?个模块后,为了让模块能够在项?中达到想要的效果,这个开发?员会??在py?件中添加?些测试信息(定义函数并调用函数), 简单示例:
def testA(a, b):
print(a + b)
testA(1, 1)
因为Python的解释型语言, 并且已经调用了这个函数, 所以不管是这个模块运行的时候, 还是其他导入了这个模块的文件运行的时候, 都会运行这段测试代码
但是一般我们不希望导入这个模块时也去运行测试代码, 因此测试代码经常是这样子写的:
def testA(a, b):
print(a + b)
if __name__ == '__main__':
testA(1, 1)
其中if __name__ == '__main__': 相当于这个模块的主函数
当导??个模块,Python解析器对模块位置的搜索顺序是:
-
当前?录 -
如果不在当前?录,Python则搜索在shell变量PYTHONPATH下的每个?录。 -
如果都找不到,Python会察看默认路径。UNIX下,默认路径?般为/usr/local/lib/python/ 模块搜索路径存储在system模块的sys.path变量中。变量?包含当前?录,PYTHONPATH和由安装过程决定的默认?录。
注意:
3.0.1 _all_列表
如果?个模块?件中有 __all__ 变量(列表),当使? from xxx import * 导?时,只能导?这个列表中的元素
同样的, 包下的_init_.py 下定义的_all_ 变量可以控制使? from xxx import *导入这个包时能导入的模块
3.1 断言函数
断言函数作用: 断言函数是对表达式布尔值的判断,要求表达式计算值必须为真。可用于自动调试 如果表达式为假,触发异常;如果表达式为真,不执行任何操作 举例:
assert(1 == 1)
assert(5 > 1)
a = "hello"
b = "hello"
assert(a == b)
assert(1 > 100)
3.2 数学函数
先引入数学模块math,引入后即可使用.以求平方根函数sqrt(x)为例:
import math
math.sqrt(1024)
常用数学函数:
函数 | 描述 |
---|
abs(x) | 返回绝对值 | ceil(x) | 往上取整,如ceil(1.1)返回2 | floor(x) | 往下取整 | exp(x) | 返回e的x次幂 | log(x) | 返回以e为底,x的对数 | pow(x,y) | 返回x的y次幂 | sqrt(x) | 返回x的平方根 | factorial(x) | 返回x的阶乘 |
3.3 随机函数
先引入random模块 下面简单介绍几个常用函数:
import random
random.random()
random.randint(1,10)
random.uniform(1,10)
3.4 range()函数和xrange()函数
- range(start,stop,step)
返回一个数字列表,包含起始值,不包含结束值 (左闭右开) 起始值可以忽略,默认为0 步长可以忽略,默认为1 - xrange()是一个类,返回一个xrange()对象,遍历后只返回一个值
3.5 turtle模块(海龟绘图法)
存在一只虚拟的海龟,让他在"画布"上通过前进后退左转右转旋转抬起放下等操作画图
4. 自带数据结构(序列)
4.1 序列概述
序列是一种数据存储方式,用来存储一系列的数据。在内存中,序列就是一块用来存放多个值的连续的内存空间
序列中存储的是整数对象的地址,而不是整数对象的值
python中常用的序列结构有:字符串、列表、元组、字典、集合
元组可以理解成不可变的列表,多数情况下使用列表
4.2 通用的序列操作
4.2.1 索引
序列中所有元素编号从0开始
可以使用负数索引,-1表示最后一个元素
对于序列字面量,可以直接执行索引操作,不用先赋值给变量
如:‘Hello’[0] 结果为’H’
list1 = [1,2,3];list[-1]为3
4.2.2 切片
索引访问单个元素,切片访问特定范围内的元素(左闭右开区间)
为此,可用俩个索引并用:隔开
如果切片开始于序列开头,可省去第一个索引
如果切片结束于序列末尾,可省去第二个索引
要指向整个序列,可将俩个索引都省略
可以同时使用正数负数索引,但前一个索引不能位于后一个索引之后,否则结果为空序列
示例:
numbers = [1,2,3,4,5,6,7,8,9,10]
numbers[0:3] # [1,2,3]
numbers[:2] # [1,2]
numbers[8:] # [9,10]
numbers[7:-1] # [8,9]
设置步长: 通常在省略步长的切片中步长默认为1,但也可以在第二个索引后加上步长, 正负整数均可:
序列[开始位置下标:结束位置下标:步?]
4.2.3 运算符
运算符规则:
集合(set)和字典(dict)都不能+和*
但是集合(set)能in和not in
4.2.3.1 相加(拼接):+
4.2.3.1 序列相加(拼接):+
可以使用+来拼接序列,但只能拼接相同类型的序列
4.2.3.2 乘法:*
可以将序列与数x相乘,将重复这个序列x次来创建新序列
示例:
[0] * 4
# [0,0,0,0]
可以使用None初始化列表(初始化长度,为空列表)
[None] * 10
4.2.3.3 成员资格:in,not in
要检查特定的值是否包含在序列中可以使用布尔运算符in in检查是否满足指定的条件,满足返回True,不满足返回False(Python中TrueFalseNone等首字母大写,与java不同) 示例:
x = 'python'
'p' in x # True
4.2.4 公共函数
4.2.4.1 获得序列的长度,最大值和最小值
len()函数返回序列包含的元素个数
max()函数返回序列中最大的元素
min()函数返回序列中最小的元素
4.2.4.2 删除
del 或 del()函数:
list1 = [1,2,3]
del list1
del(list1)
del list1[0]
4.2.4.3 enumerate()函数
enumerate(可遍历对象, start)函数?于将?个可遍历的数据对象(如列表、元组或字符串)组合为?个索引序列(生成一个元组),同时列出数据和数据下标,?般?在 for 循环当中, start参数?来设置遍历数据的下标的起始值,默认为0
示例:
list1 = [1, 2]
for i in enumerate(list1):
print(type(i))
print(i)
for index, char in enumerate(list1, start=1):
print(type(index))
print(type(char))
print(f'下标是{index}, 对应的字符是{char}')
结果:
4.3 str(字符串)
严格意义上来讲, 字符串并不是python的序列, 但是很多序列的语法用在字符串上也都是适用的, 因此放在这里
python常用API:
4.3.1 查找
所谓字符串查找?法即是查找?串在字符串中的位置或出现的次数:
4.3.1.1 find
fifind():检测某个?串是否包含在这个字符串中,如果在返回这个?串开始的位置下标,否则则返回-1:
字符串序列.find(?串, 开始位置下标, 结束位置下标)
注意:开始和结束位置下标可以省略,表示在整个字符串序列中查找
mystr = "hello world and Python and Java"
print(mystr.find('and'))
print(mystr.find('and', 15, 30))
print(mystr.find('ands'))
4.3.1.2 index()
index():检测某个?串是否包含在这个字符串中,如果在返回这个?串开始的位置下标,否则则报异常:
字符串序列.index(?串, 开始位置下标, 结束位置下标)
注意:开始和结束位置下标可以省略,表示在整个字符串序列中查找
mystr = "hello world and Python and Java"
print(mystr.index('and'))
print(mystr.index('and', 15, 30))
print(mystr.index('ands'))
4.3.1.3 rfind()和rindex()
rfind(): 和find()功能相同,但查找?向为右侧开始
rindex():和index()功能相同,但查找?向为右侧开始
mystr = "hello world and Python and Java"
print(mystr.rfind('and'))
print(mystr.rfind('and', 15, 30))
print(mystr.rfind('ands'))
mystr = "hello world and Python and Java"
print(mystr.rindex('and'))
print(mystr.rindex('and', 15, 30))
4.3.1.4 count()
count():返回某个?串在字符串中出现的次数:
字符串序列.count(?串, 开始位置下标, 结束位置下标)
注意:开始和结束位置下标可以省略,表示在整个字符串序列中查找
mystr = "hello world and Python and Java"
print(mystr.count('and'))
print(mystr.count('and', 15, 30))
print(mystr.count('ands'))
4.3.2 修改
所谓修改字符串,指的就是通过函数的形式修改字符串中的数据
4.3.2.1 replace():替换
字符串序列.replace(旧?串, 新?串, 替换次数)
注意:替换次数如果查出?串出现次数,则替换次数全部出现次数
返回修改后的字符串
同时数据按照是否能直接修改分为可变类型和不可变类型两种。字符串类型属于不能直接修改数据的类型, 即不可变类型, 因此使用replace修改后原字符串不变
mystr = "hello world and Python and Java"
print(mystr.replace('and', 'he'))
print(mystr.replace('and', 'he', 1))
print(mystr)
4.3.2.2 join():合并字符串
join():??个字符或?串合并字符串,即是将多个字符串合并为?个新的字符串:
字符或?串.join(多字符串组成的序列)
list1 = ['hello', 'world', 'and', 'python']
t1 = ('aa', 'b', 'cc', 'ddd')
print('_'.join(list1))
str = '...'
print(str.join(t1))
和直接序列相加不一样
4.3.2.3 大小写转换
title():将字符串每个单词?字?转换成?写
都是返回修改后的字符串,原字符串不变
str = 'hello world'
print(str.title())
print(str)
lower():将字符串中全部?写转?写
str = 'Hello woRld'
print(str.lower())
upper():将字符串中全部?写转?写
str = 'Hello woRld'
print(str.upper())
4.3.2.4
4.3.3 分隔:split()
split():按照指定字符分割字符串:
字符串序列.split(分割字符, num)
注意:返回分割后的列表
num表示的是分割字符出现的次数,即将来返回数据个数为num+1个
如果没指定num则将字符串按照全部分割字符切分
mystr = "hello world and Python and Java"
print(mystr.split('and'))
print(mystr.split('and', 1))
print(mystr.split(' ', 2))
print(mystr)
4.4 list(列表ArrayList)
4.4.1 概述
列表:用于存储任意数目、任意类型的数据集合。
列表是内置可变序列,是包含多个元素的有序连续的内存空间
列表定义的标准语法格式:
a = [10,20,30,40]
其中,10,20,30,40 这些称为:列表 a 的元素。
列表中的元素可以各不相同,可以是任意类型。比如:
a = [10,20,'abc',True]
python的序列全都支持元素以各不相同,任意类型
Python 的列表大小可变,根据需要随时增加或缩小
添加元素可以直接使用索引也可以使用append()方法或者extend方法,insert()方法
list可以修改指定下标数据:
name_list = ['Tom', 'Lily', 'Rose']
name_list[0] = 'aaa'
print(name_list)
4.4.2 API
方法 | 要点 | 描述 |
---|
list.append(x) | 增加元素 | 在list的尾部插入x | list.extend(aList) | 增加元素 | 将列表aList的所有元素加到list的尾部 | list.insert(index,x) | 增加元素 | 在指定位置index加入元素x | list.remove(x) | 删除元素 | 在列表list中删除首次出现的指定元素x | del list[index] | 删除元素 | 删除列表指定位置的元素 | list.pop() | 删除元素 | 移除元素,默认从最后移除,返回该元素值;括号中可加入元素索引值来移除 | list.clear() | 删除所有元素 | 删除列表所有元素,并不是删除列表对象 | list.index(x) | 访问元素 | 返回第一个元素x的索引位置,不存在x元素时抛出异常 | list.count(x) | 计数 | 返回元素x在list中出现的次数 | len(list) | 列表长度 | 返回列表中包含的元素个数 | list.sort() | 排序 | 所有元素原地排序 | list.sorted() | 排序 | 建立一个副本再排序,返回副本列表,原列表不变 | list.reverse() | 颠倒顺序 | 按相反顺序排列列表中的元素,不返回任何值,如[1,2]变成[2,1] | list.copy() | 复制列表 | 常规复制方法只是将另一个名称关联到列表,copy()方法关联到列表的副本 |
方法sort和sorted接受两个参数:key和reverse,这俩个参数通常按名称指定 多数情况下,将key设置为一个自定义函数 参数reverse设置为True时将反序排列列表,如[8,4,1]这样子的降序排序
4.4.3 其他创建方式
list()创建列表, range()创建列表 使用 list()可以将任何可迭代的数据转化成列表。 range()可以帮助我们非常方便的创建整数列表,语法格式为:
range([start,] end [,step])
示例:
a = list(range(10)) # 0到9
b = list(range(0.20,2)) # 0到20,步长为2
4.4.4 遍历
while:
name_list = ['Tom', 'Lily', 'Rose']
i = 0
while i < len(name_list):
print(name_list[i])
i += 1
for:
name_list = ['Tom', 'Lily', 'Rose']
for i in name_list:
print(i)
4.4.5 列表嵌套
相当于二维数组
name_list = [['?明', '?红', '?绿'], ['Tom', 'Lily', 'Rose'], ['张三', '李四', '王五']]
print(name_list[2][1])
4.5 tuple(元组不可变list)
相当于 不可变的list列表
通过()创建元组。小括号可以省略
a = (10,20,30)或者a = 10,20,30
**注意:**如果元组只有一个元素, 则必须后面加逗号
这是因为解释器会把(1)解释为整数 1, (1,)解释为元组。
元组中的元素值是不允许修改的,但我们可以对元组进行连接组合: 示例:
a = (1,2,3,5)
b = (6,7)
c = a + b
print(c)
a = a[:3] + (4,) + a[3:]
print(a)
元组可以用作映射(字典)中的键,以及集合(set)中的成员,而列表不行, 因为列表等是可变数据类型
元组数据不?持修改,只?持查找,具体如下:
4.6 dict(字典)(映射map)
4.6.1 概述
相当于java的map
映射: 通过名称来访问各个值的数据结构
字典是python中唯一的内置映射类型,其各个值不按顺序排列,而是存储在键下,即键值对
键可以是数,字符串或元组等不可变的类型, 不能是列表(list),集合(set),字典(dict)等可变类型 字典中值可以重复,但键必须是唯一的(映射)
4.6.2 创建字典
-
直接创建 字典放在花括号{}中,键和值之间用冒号(:)分隔,每个键值对之间用逗号(,)分隔 示例:
a = {'x': 1,'y' : 2}
a = {}
-
使用dict函数(实际上dict不是函数,而是一个类) 可使用函数dict从其他映射或键值对序列来创建字典 示例:
items = [('name','Gumby'),('age',10)]
d = dict(items)
? 还可以使用关键字参数来调用这个函数,如下:
# {'age':10,'name':'Gumby'}
d = dict(name = 'Gumby',age = 10)
# 创建空字典:
c = dict()
4.6.3 字典的基本操作
字典序列[key] = 值有则覆盖, 无则新增
API:
函数 | 描述 |
---|
len(dict) | 返回键值对个数 | del dict[k] | 删除键为k的项 | k in dict | 检查字典中是否包含键为k的项 | dict.clear() | 删除字典中的所有项,不返回值 | dict.copy() | 返回一个键值对与原来字典相同的新字典,但这个方法是浅复制,即值是原件不是副本,修改新字典的值,原件受影响 | dict.deepcopy() | 深复制,但deepcopy是模块copy中的函数,需要导入 | dict.get(k) | 通过键访问值,这种方法相较于直接访问更宽松(因为可以访问不存在的键,返回None) | dict.items() | 以元组的方式返回键值对 | dict.gets() | | dict.values() | |
dict.gets():
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
print(dict1.keys())
dict.values():
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
print(dict1.values())
dict.gets():
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
'男')])
print(dict1.items())
4.6.4 遍历
dict1 = {'name': 'Tom', 'age': 20, 'gender': '男'}
for key in dict1.keys():
print(key)
for value in dict1.values():
print(value)
for item in dict1.items():
print(item)
for key, value in dict1.items():
print(f'{key} = {value}')
4.7 set(集合HashSet)
4.7.1 概述
创建集合使? {} 或 set() , 但是如果要创建空集合只能使? set() ,因为 {} ?来创建空字典
集合中的每一个元素不能出现多次,并且是无序存储的,相当于Java的HashSet
因此集合没有+,*等操作
同时也不能索引,切片
s1 = {10, 30, 20, 10, 30, 40, 30, 50}
print(s1)
s2 = set('abcdefg')
print(s2)
s3 = set()
print(s3)
print(type(s3))
s4 = {}
print(s4)
print(type(s4))
注意:
集合可以快速完成列表去重
但集合不?持下标
4.7.2 增加数据
-
add(),参数是基本数据类型 当向集合内追加的数据是当前集合已有数据的话,则不进?任何操作 返回None -
update(), 追加数据, 参数序列 返回None -
remove(),删除集合中的指定数据,如果数据不存在则报错 -
discard(),删除集合中的指定数据,如果数据不存在也不会报错 -
pop(),随机删除集合中的某个数据,并返回这个数据
4.7.3 in和not in
集合set是可以进行in和not in,前面表格有误
4.8 推导式
只有列表,集合和字典有推导式
4.8.1 列表推导式
列表推导式:??个表达式创建?个有规律的列表或控制?个有规律列表。
列表推导式?叫列表?成式
如果不使用推导式创建有规律的列表, 例如创建?个0-10的列表:
list1 = []
while i < 10:
list1.append(i)
i += 1
print(list1)
for i in range(10):
list1.append(i)
但如果使用了推导式:
list1 = [i for i in range(10)]
解释:
示例:
- 可以通过range参数获得指定规律的列表, 例如0-10的偶数列表:
list1 = [i for i in range(0, 10, 2)]
- 通过if语句获得指定规律的列表, 还是0-10的偶数列表:
list1 = [i for i in range(10) if i % 2 == 0]
- 希望列表中存放序列, 使用多个for, 例如存放元组:
list1 = [(i, j) for i in range(1, 3) for j in range(3)]
4.8.2 字典推导式
字典推导式作?:快速合并列表为字典或创建?个有规律的字典或提取字典中?标数据
- 将两个列表合并为?个字典:
list1 = ['name', 'age', 'gender']
list2 = ['Tom', 20, 'man']
dict1 = {list1[i]: list2[i] for i in range(len(list1))}
- 创建?个有规律的字典
dict1 = {i: i**2 for i in range(1, 5)}
print(dict1)
- 提取字典中?标数据
counts = {'MBP': 268, 'HP': 125, 'DELL': 201, 'Lenovo': 199, 'acer': 99}
count1 = {key: value for key, value in counts.items() if value >= 200}
print(count1)
4.8.3 集合推导式
集合推导式作?:创建?个有规律的集合
list1 = [1, 1, 2]
set1 = {i ** 2 for i in list1}
print(set1)
4.9 迭代器
迭代是python中访问集合元素的一种非常强大的一种方式。迭代器是一个可以记住遍历位置的对象,因此不会像列表那样一次性全部生成,而是可以等到用的时候才生成,因此节省了大量的内存资源。迭代器对象从集合中的第一个元素开始访问,直到所有的元素被访问完。迭代器有两个方法:__iter__() 和__next()__ 方法
类似于list、tuple、str 等类型的数据可以使用for …… in…… 的循环遍历语法从其中依次拿到数据并进行使用,我们把这个过程称为遍历,也称迭代。python中可迭代的对象有**list(列表)、tuple(元组)、dirt(字典)、str(字符串)、set(集合)**等
创建一个可迭代的对象:只要此对象含有**iter方法,那么它就是一个可迭代的对象**, isinstance()函数以及Iterable来判断一个对象是否是可迭代的对象, 是一个**Iterable(可迭代对象)**则结果返回为True;否则,结果为False
print("判断是否是可迭代的对象:", isinstance(class1,Iterable))
创建迭代器:一个类(对象)只要含有“iter”、"next"两个方法,就将其称为迭代器。__iter__方法返回一个特殊的迭代器对象,而这个迭代器对象自动实现了_next__方法,并返回一个值,最后通过抛出异常StopIteration来结束迭代,
同样可以通过isinstance()函数以及Iterator判断是否是迭代器
print("判断是否是迭代器:", isinstance(class1,Iterator))
迭代器代码示例:
class Classmate(object):
"""定义一个同学类"""
def __init__(self):
self.name = list()
self.name_num = 0
def add(self,name):
self.name.append(name)
def __iter__(self):
return self
def __next__(self):
if self.name_num < len(self.name):
ret = self.name[self.name_num]
self.name_num += 1
return ret
else:
raise StopIteration
迭代器最核心的功能就是可以通过**__next__方法的调用来返回下一个值**。而这个值不是从已有的数据中读取的,而是通过程序按照一定的规则生成的。这也就意味着我们可以不再依赖一个现存的数据集合来存放数据,而是边用边生成,这样的好处就是可以节省大量的内存空间
以实现斐波那契数列(0,1,1,2,3,5,8,13,21……后一项总是等于前两项的和)为例:
方法一:
a = 0
b = 1
myFibonacci = list()
nums = int(input("请输入需要生成Fibonacci数列项的个数:"))
i = 0
while i < nums:
myFabonacci.append(a)
a,b = b,a+b
i += 1
for num in nums:
print(num)
方法二:
class Fibonacci(object):
"""斐波那契数列得迭代器"""
def __init__(self,nums):
self.nums = nums
self.a = 0
self.b = 1
self.i =0
def __iter__(self):
return self
def __next__(self):
ret = self.a
if self.i < self.nums:
self.a, self.b = self.b,self.a +self.b
self.i += 1
return ret
else:
raise StopIteration
nums = int(input("请输入需要生成Fibonacci数列项的个数:"))
fobo = Fibonacci(nums)
for num in fobo:
print(num)
虽然结果相同,但实际效果却相差巨大。来看一下方法一,他是通过while循环立即生成一个列表用来存放数据,接着再从已有的数据中读取所需数据,而这需要占用一定的内存空间;再来看一下方法二,它并没有用到列表,而是返回一个迭代器,在需要的时候生成相关数据。纵观以上两种方法,在生成个数较小时,两者相差不大,但当生成个数是10万,100万,1000万呢?前者需要消耗大量的内存资源,而后者仅需占用一点内存即可。这也是python2中range()函数和python3中range()函数的不同点,python3的range()函数采用了迭代器的方式,不再依赖于现有的数据集合,也就相当于python2中的xrange()函数
总结:
可迭代对象不一定是迭代器。
迭代器一定是可迭代对象。
容器类型(list tuple dict str set )是可迭代对象但不是迭代器。
4.10 拆包
对字典进?拆包,取出来的是字典的key,方便我们操作value
def return_num():
return 100, 200
num1, num2 = return_num()
print(num1)
print(num2)
dict1 = {'name': 'TOM', 'age': 18} a, b = dict1
print(a)
print(b)
print(dict1[a])
print(dict1[b])
5. 基础++语法
5.1 函数
5.1.1 概述
函数通过关键字def定义,def后跟函数名与圆括号,括号内放形参,以冒号结尾.
def sum(a, b):
return a+b
-
变量作用域:全局变量与局部变量: 一般在函数体外声明的都是全局变量,在函数内部声明的都是局部变量 所以为了在函数中使用全局变量(修改),就得使用global语句, 不然操作的是局部变量 global语句相对于在函数内部或循环内部声明变量是全局变量,使得函数内可以使用和改变全局变量
a = 1
def sum():
global a
a = 2
sum()
print(a)
-
可以在函数体最前面使用多行注释写函数的说明文档 使用help(函数名)即可查看说明文档 -
return语句 跳出函数并返回一个值 没有返回值的return语句相当于return None return a, b 写法,返回多个数据的时候,默认是元组类型 return后?可以连接列表、元组或字典,以返回多个值 -
直接打印方法名不加括号,打印结果: <function sum at 0x000002291D63B948> -
默认参数 通过声明形参时为一些参数赋值使一些参数是"可选的,有默认值的"
def say(msg, times = 1):
print(msg * times)
say("Peter")
say("Peter", 3)
-
关键参数 调用方法时为参数赋值默认是按照定义顺序赋值的 但当参数很多时,且只想指定其中的部分,可以在调用函数时为这些参数赋值(通过参数名= 值) 优点:不用担心参数顺序.且假设其他参数都有默认值时可以只给我们想要的那些参数赋值
def func(a, b=2, c=3)
print(a,b,c)
func(1)
func(1, 5)
func(1, c=10)
func(c=20, a=30)
-
收集参数 -
在形参前加上*,会收集所有剩下的参数(未被指定的非关键参数)到一个元组中 如果没有可供收集的参数,该参数将是一个空元组 带星号参数后面也可以继续放参数,此时调用函数时对后面参数要使用名称来指定(关键参数) 星号不会收集关键字参数 要收集关键参数可以使用**,此时该参数收集为一个字典 示例:
def demo_1(text, *s):
print(text)
print(s)
demo_1(sss,s1,s2)
/* 结果:
sss
(s1,s2)
*/
def demo_2(text, *s,text2):
print(text)
print(s)
print(text)
demo_2(sss,s1,s2,text2 = t)
/*结果:
sss
(s1,s2)
t
*/
def demo_3(**s):
print(s)
demo_3(x = 1, y = 2, z = 3)
# 结果:{'z':3,'y':2,'x':1}
5.1.2 引用
字符串,数和元组(tuple)等不可变的数据类型为参数时形参不变
列表(list)等可变的数据类型为参数时,实参改变
这是因为在python中,值是靠引?来传递来的
可以通过id()来得到变量在内存中的地址
这就相当于Java的实参为基本数据类型时实参不变, 为引用数据类型时实参改变
a = 1
b = a
print(b)
print(id(a))
print(id(b))
a = 2
print(b)
print(id(a))
print(id(b))
aa = [10, 20]
bb = aa
print(id(aa))
print(id(bb))
aa.append(30)
print(bb)
print(id(aa))
print(id(bb))
5.1.3 lambda表达式:
如果?个函数有?个返回值,并且只有?句代码,可以使? lambda简化:
lambda 参数列表 : 表达式
lambda表达式的参数可有可?,函数的参数在lambda表达式中完全适?。
lambda函数能接收任何数量的参数但只能返回?个表达式的值
直接打印lambda表达式,输出的是此lambda的内存地址
示例:
def fn1():
return 200
fn2 = lambda: 100
lambda a, b: a + b
print((lambda a, b: a + b)(1, 2))
应用示例:
students = [
{'name': 'TOM', 'age': 20},
{'name': 'ROSE', 'age': 19},
{'name': 'Jack', 'age': 22}
]
students.sort(key=lambda x: x['name'])
print(students)
students.sort(key=lambda x: x['name'], reverse=True)
print(students)
students.sort(key=lambda x: x['age'])
print(students)
**注意:**和Java的lambda表达式是不一样的
5.1.4 高阶函数
把函数作为参数传?,这样的函数称为?阶函数,?阶函数是函数式编程的体现。函数式编程就是指这种?度抽象的编程范式
示例:传入abs绝对值函数:
def sum_num(a, b, f):
return f(a) + f(b)
result = sum_num(-1, 2, abs)
print(result)
python内置的高阶函数:
5.1.4.1 map(func, Iterator)
map(func, lst),将传?的函数变量func作?到Iterator迭代器变量的每个元素中,并将结果组成迭代器(Python3)返回, 示例:计算 list1 序列中各个数字的2次?
list1 = [1, 2, 3, 4, 5]
def func(x):
return x ** 2
result = map(func, list1)
print(result)
print(list(result))
5.1.4.2 reduce(func(x,y),lst)
reduce(func(x,y),lst),其中func必须有两个参数。每次func计算的结果(第一次运算是序列中前俩个元素)继续和序列的下?个元素做累积计算
示例:计算 list1 序列中各个数字的累加和
import functools
list1 = [1, 2, 3, 4, 5]
def func(a, b):
return a + b
result = functools.reduce(func, list1)
print(result)
5.1.4.3 fifilter(func, lst)
fifilter(func, lst)函数?于过滤序列, 过滤掉不符合条件(func)的元素, 返回?个 fifilter 对象,。如果要转换为列表,可以使? list() 来转换:
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def func(x):
return x % 2 == 0
result = filter(func, list1)
print(result)
print(list(result))
5.2 文件IO
5.2.1 打开/关闭文件
使?open函数,可以打开?个已经存在的?件,或者创建?个新?件,语法如下:
open(name, mode)
返回的是 open 函数的?件对象
name:是要打开的?标?件名的字符串(可以包含?件所在的具体路径)。
mode:设置打开?件的模式(访问模式):只读、写?、追加等
mode取值:
关闭文件:
文件对象.close()
使用完文件最后在程序最后关闭文件释放内存
5.2.2 操作文件
5.2.2.1 写:write(‘内容’)
文件对象.write('内容')
5.2.2.2 读
?件对象.read(num)
num表示要从?件中读取的数据的?度(单位是字节),如果没有传?num,那么就表示读取?件中所有的数据
-
readlines() readlines可以按照?的?式把整个?件中的内容进??次性读取,并且返回的是?个列表,其中每?? 的数据为?个元素 无参数 -
readline() readline()?次读取??内容, 刚开始指针指向文件的第一行, 读取完之后指针往下移一行 -
seek(偏移量, 起始位置) 作?:?来移动?件指针 起始位置取值:
0:?件开头
1:当前位置
2:?件结尾
可以通过这些读写操作实现文件复制,备份等
5.2.3 操作文件和文件夹
在Python中?件和?件夹的操作要借助os模块??的相关功能:
import os
os.rename(?标?件名, 新?件名)
os.remove(?标?件名)
os.mkdir(?件夹名字)
os.rmdir(?件夹名字)
os.getcwd()
os.chdir(?录)
os.listdir(?录)
5.3 异常处理
python异常处理语法与Java不同
同时:python的异常可以套娃
try:
可能发?错误1的代码
try:
可能发?错误2的代码
except:
如果出现异常2执?的代码
except:
如果出现异常1执?的代码
5.3.1 捕获指定异常:
try:
可能发?错误的代码
except 异常类型:
如果捕获到该异常类型执?的代码
try:
print(num)
except NameError:
print('有错误')
5.3.2 捕获多个异常:
当捕获多个异常时,可以把要捕获的异常类型的名字,放到except 后,并使?元组的?式进?书写
try:
print(1/0)
except (NameError, ZeroDivisionError):
print('有错误')
5.3.3 捕获异常描述信息
try:
print(num)
except (NameError, ZeroDivisionError) as result:
print(result)
5.3.4 捕获所有异常
Exception是所有程序异常类的?类
try:
print(num)
except Exception as result:
print(result)
5.3.5 异常else
else表示的是如果没有异常要执?的代码
try:
print(1)
except Exception as result:
print(result)
else:
print('我是else,是没有异常的时候执?的代码')
5.3.6 异常finally
fifinally表示的是?论是否异常都要执?的代码, 和Java类似, 常用于释放资源:
try:
f = open('test.txt', 'r')
except Exception as result:
f = open('test.txt', 'w')
else:
print('没有异常,真开?')
finally:
f.close()
5.3.7 自定义异常:
在Python中,抛出?定义异常的语法为 raise 异常类对象
示例:
# ?定义异常类,继承Exception
class ShortInputError(Exception):
def __init__(self, length, min_len):
self.length = length
self.min_len = min_len
# 设置抛出异常的描述信息
def __str__(self):
return f'你输?的?度是{self.length}, 不能少于{self.min_len}个字符'
def main():
try:
con = input('请输?密码:')
if len(con) < 3:
raise ShortInputError(len(con), 3)
except Exception as result:
print(result)
else:
print('密码已经输?完成')
main()
5.4 包
包将有联系的模块组织在?起,即放到同?个?件夹下,并且在这个?件夹创建?个名字为 init.py ?件,那么这个?件夹就称之为包, 这个?件控制着包的导??为
导入包分俩种:
5.4.1 导入包1
import 包名.模块名
from 包名 import 模块名
使用这种方法导入包只能通过包名.模块名.功能()调用
注意:
? 这里的导入和前面导入模块是不同的, 不能写*
? 但是可以使用as起别名, 但是哪怕是起别名, 如果名字冲突还是会报错
? 如果在相同包下, 则使用前面的导入模块语法, 使用这里的导入包语法也是会报错的
5.4.2 导入包2
**注意:**必须先在 __init__.py ?件中添加 __all__ = ['允许导入的模块名'] ,控制允许导?的模块列表,才可以使用这种方式:
from 包名 import *
像NumPy和sklearn等都是加上了这个
也就是说, 加上这个之后, from xxx import xxx就有俩种不同的含义了(从模块中导入方法, 从包中导入模块…)
6. 面向对象编程
什么是面向对象就不说了, 自己多敲点代码, 买本Java编程思想看吧
Python的面向对象也有的三大特性:封装, 继承, 多态
6.1 类
在python中不需要方法重载, 因为调用方法时参数是可以随意来的
6.1.1 经典方法定义类
经典方法是指类名后不跟():
class ClassName:
...
6.1.2 新式方法定义类:
新式方法是指在类名后跟(类名), 表示继承:
class Prentice(objec):
pass
在Python中,所有类默认继承object类,object类是顶级类或基类;其他?类叫做派?类
在子类中未定义构造函数,则创建子类对象默认调用父类中的构造函数
在子类中定义构造函数,则创建子类对象调用的是子类中的构造函数,不会主动去调父类的构造函数, 这和java不一样
但为了继承的时候父类的属性等能被初始化, 一般我们都会在子类__init__ 构造函数中调用父类构造函数(如果需要继承父类中的属性,则需要调用父类的构造函数)
注意:
- 在python中支持多继承!!!当?个类有多个?类的时候,默认使?第?个?类的同名属性和?法,包括没写子类构造函数时也是默认调用第一个
- 也因为python多继承加上经常儿孙满堂, 所以Python的继承结构异常复杂, 继承时的方法调用也异常恶心(出现了一个继承树…)
class Prentice(School, Master):
pass
调用父类构造函数:
super(子类名, self).__init__(参数列表)
super().__init__(参数列表)
父类名.__init__(self, 参数列表)
super().函数名(参数列表)
注意:在单继承中,三种方式都可以使用;在多继承中,只能使用方式三
继承的特点
- 子类对象可以直接访问父类中未被私有化的属性和函数
- 父类的
__slots__ 属性对子类不起作用 - 父类对象不能访问子类中特有的属性和函数
6.1.2.1 重写
重写基本和Java一致
?类和?类具有同名属性和?法,默认使??类的同名属性和?法
6.1.3 self
python中的类中的方法经常有这么一个参数:self, self指的是调?该函数的对象, 相当于Java中的this
实例函数的形参列表第一个参数都是self,而普通函数则不是
6.1.4 封装
私有属性:只能在类的内部被直接访问,在对象属性的前面添加两个下划线表示是私有属性, 一般将私有属性写在构造函数__init__ 中, 直接写在类里也可以(一般将属性写在类下面或者__init__ 中)
工作原理:一个属性一旦被私有化,在底层形成了 _类名__属性名 的属性名 ,但是不建议使用。私有化属性在底层的存在形式根据操作系统或者Python解释器的不同会有所差别,如果直接使用此种方式访问,违背了Python跨平台的特点
私有化之后在对外提供get/set方法, 一般命名:get_xx
同样的, 当类中的函数名前面加上__时表示私有函数, 只能被类内部调用
有用的一些__属性:__doc__ , __all__ (上面讲过), __slots__ (下面讲), __dict__ , __module__ , __class__ :
-
__doc__ 表示类的描述信息,获取类中的文档注释(多行注释) 类的描述信息类似于函数, 也是写在类体最前面的多行注释 但是方法文档通过help(方法名)获取, 类文档通过这个属性获取 不需要重写 -
__dict__ 获取类或者对象的信息【属性和方法】,返回字典: 也不需要重写
class MyClass(object):
num = 10
def __init__(self, m):
self.m = m
def show(self):
pass
@classmethod
def func(cls):
pass
@staticmethod
def test():
pass
print(MyClass.__dict__)
m = MyClass(5)
print(m.__dict__)
-
__module__ 获取当前操作的对象在哪个模块 如果被操作的对象在当前模块,则获取的结果为__main__ ,如果被操作的对象在其他模块,则获取的结果为 模块名 不需要重写 -
__class__ 类似于type(xxx) ,返回当前对象的类型
6.1.5 魔法方法
__xx__() 一般在python中称为魔法方法,都是具有特殊功能的函数, 一般都是从object那里重写来的
6.1.5.1 __new__ ,__init__ 和 __del__()
工作原理:
-
__new__ :创建对象的时候首先自动调用__new__ ,它的作用就是创建实例,然后将该实例返回 -
__init__ :当实例创建完毕之后被调用的,然后通过__init__ 给实例设置初始值 -
__new__ 先被调用,__init__ 后被调用,__new__ 的返回值(实例)将传递给__init__ 的第一个参数self,然后__init__ 给这个实例设置一些参数,__init__ 不需要返回值 -
严格意义上讲__init__ 才是构造方法, __new__ 不是 -
当对象被销毁的时候自动调用的函数,称为析构函数,析构函数为 __del__() 当对象被定义为全局变量时,程序执行完毕,对象自动被销毁 当对象被定义为局部变量,当指定的函数执行完毕,则对象随着会被自动销毁 强制销毁对象,当对象被del时, 销毁对象执行析构函数 __del__()
6.1.5.2 __str__ >__repr__
当使?print输出对象的时候,默认打印对象的内存地址。如果类定义了 __str__ ?法,那么就会打印从在这个?法中 return 的数据, 相当于Java中的重写toString()
__repr__ 方法 和 __str__ 方法功能类似,都是用来修改一个对象的默认打印内容。在打印一个对象时,如果没有重写 __str__ 方法,它会自动来查找 __repr__ 方法。如果这两个方法都没有,会直接打印这个对象的内存地址。如果两个方法都写了,选择 __str__ 方法
6.1.5.3 __eq__
__eq__ 方法如果不重写,默认比较依然是内存地址
6.1.5.4 汇总
运算符相关的一系列魔法方法:
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
return self.name == other.name and self.age == other.age
def __ne__(self, other):
return self.age != other.age
def __lt__(self, other):
return self.age < other.age
def __gt__(self, other):
return self.age > other.age
def __le__(self, other):
return self.age <= other.age
def __ge__(self, other):
return self.age >= other.age
算数运算符相关魔法方法:
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __add__(self, other):
return self.age + other
def __sub__(self, other):
return self.age - other
def __mul__(self, other):
return self.age * other
def __truediv__(self, other):
return self.age / other
def __mod__(self, other):
return self.age % other
def __pow__(self, power, modulo=None):
return self.age ** power
s = Student('zhangsan', 18)
print(s + 1) # 19
print(s - 2) # 16
print(s * 2) # 36
print(s / 5) # 3.6
print(s % 5) # 3
print(s ** 2) # 324
类型转换相关魔法方法:
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __int__(self):
return self.age
def __float__(self):
return self.age * 1.0
def __str__(self):
return self.name
def __bool__(self):
return self.age > 18
s = Student('zhangsan', 18)
print(int(s))
print(float(s))
print(str(s))
print(bool(s))
6.2 对象
dir() 函数可以查看对象内的所有的属性和方法
6.2.1 创建对象:
对象名 = 类名()
调用类中的函数实, 如果通过类名调(必须手动传self):
类名.方法名(self, 参数)
p = Person()
Person.eat(p,'吃饭')
如果通过对象调, 则不用传self,会自动传入:
p.eat('吃饭')
6.2.2 对象属性
属性即成员变量, 在python中的对象属性即可以在类中添加获取, 也可以在类外面添加获取
在类外面获取对象属性(和Java一样):
对象名.属性
在类外面添加属性(Java没有)
对象名.属性 = 值
但是这种方法绑定的属性的数量没有上限,绑定的属性的名称没有限制,在实际项目开发中,需要进行限制绑定
限制绑定方法:
在类中增加一个属性:__slots__=(属性1, 属性2..) (俩个_)
这样子在类外增加属性时, 如果属性名在__slots__ 中没有, 则会报错
注意:
__slots__ 中定义的属性仅对当前类的实例起作用,对继承的子类实例是不起作用- 如果子类本身也有
__slots__ 属性,子类的属性就是自身的 __slots__ 加上父类的 __slots__
同时在类里面设置的属性相当于静态的, 是类所有的, 所有的对象共享同一个(类属性只能通过类对象修改,不能通过实例对象修改,如果通过实例对象修改类属性,表示的是创建了?个实例属性:
class A:
a = 1
def __init__(self, b):
print("A初始化")
self.a = b
a = A('a')
print(a.a)
b = A('b')
print(b.a)
print(a.a)
但是在类外设置的属性就是属于当前对象的
在类里面获取属性(相当于java中的this.属性, 但是Java的this可以省略, python不可以省略self):
self.属性
6.3 多态
不同的子类调用相同的父类方法,产生不同的执行结果,可以增加代码的外部灵活度。多态是以继承和重写父类方法为前提的,它是一种调用方法的技巧,不会影响到类的内部设计
|