爆肝六万字整理的python基础,快速入门python的首选

爆肝六万字整理的python基础,快速入门python的首选Python基础大全1环境安装学习python开发,首先要学会安装Python环境,我一般使用Anaconda+Pycharm作为开发环境。Anaconda是一个开源的Python发行版本,其包含了conda、Python等180多个科学包及其依赖项。因为包含了大量的科学包,Anaconda的下载文件比较大(约531MB),如果觉得安装包太大可以使用Miniconda。关于环境的安装我已经写了多篇文章,比如:Ubuntu20.04的开发环境搭建:(4条消息)Ubuntu20.04安装A

大家好,又见面了,我是你们的朋友全栈君。

文章目录

1 环境安装

学习python开发,首先要学会安装Python环境,我一般使用Anaconda+Pycharm作为开发环境。Anaconda是一个开源的Python发行版本,其包含了conda、Python等180多个科学包及其依赖项。因为包含了大量的科学包,Anaconda 的下载文件比较大(约 531 MB),如果觉得安装包太大可以使用Miniconda。

关于环境的安装我已经写了多篇文章,比如:

Ubuntu20.04的开发环境搭建:(4条消息) Ubuntu 20.04安装 Anaconda和Pycharm_AI浩-CSDN博客

Win10的开发环境搭建:(4条消息) Win10 安装Anaconda、Pycharm、Tensorflow和Pytorch_AI浩-CSDN博客

Anaconda的历史版本下载:(4条消息) Anaconda历史版本_AI浩-CSDN博客,如果不想使用最新的版本,可以找到以前的版本。

2 Python的第一程序Hello World

新建个工程,将其命名为“Python基础”,然后点击“Create”

image-20210826100136979

然后,右击工程,选择“New”,然后选择“Python File”。

image-20210826100257765

将其命名为“第一个Python程序”,然后按“Enter”键

image-20210826100510181

到这里我们创建Python文件的工作就完成成了,下面试试Hello World。

将下面的代码,复制进去:

   print('hello world')

然后右键选择Run,或者使用快捷键“Ctrl+Shift+F10″。

image-20210826100825453

运行结果:

image-20210826100952958

喜欢敲命令的朋友可以选择命令行运行:

python 第一个Python程序.py

image-20210826101134469

3 变量定义和类型

3.1 什么是变量。

打个比方:我最近买了两本书,一本售价89,一本售价118,我想用python计算一下两本书一共要多少钱:

num1 = 89
num2 = 118
num3 = num1 + num2
print(num3)

运行结果:207

所谓的变量就是对计算机存储值的表示,从上面的例子,我们可以得出:

Python 中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。

在 Python 中,变量没有类型,我们所说的”类型”是变量所指的内存中对象的类型。

等号(=)用来给变量赋值。

等号(=)运算符左边是一个变量名,等号(=)运算符右边是存储在变量中的值。

3.2 变量的类型

请添加图片描述

如何知道一个变量的类型呢?

在python中,只要定义了一个变量,而且它有数据,那么它的类型就已经确定了,不需要咱们开发者主动的去说明它的类型,系统会自动辨别

可以使用type(变量的名字),来查看变量的类型,例如:

num1 = 89
num2 = 118
num3 = num1 + num2
print(type(num3))

运行结果:<class ‘int’>

3.3 变量的命名规则

变量的命名规则:

  1. 由字母、下划线和数字组成,且数字不能开头。

  2. 不能使用python的关键字。

​ 关键字有:

      and     as      assert     break     class      continue    def     del
      elif    else    except     exec      finally    for         from    global
      if      in      import     is        lambda     not         or      pass
      print   raise   return     try       while      with        yield
  1. 起一个有意义的名字,尽量做到看一眼就知道是什么意思(提高代码可 读性) 比如: 名字 就定义为 name ,年龄用age。

  2. 驼峰命名法

第一种 小驼峰式命名法(lower camel case): 第一个单词以小写字母开始;第二个单词的首字母大写,例如:myName、aDog

第二种 大驼峰式命名法(upper camel case): 每一个单字的首字母都采用大写字母,例如:FirstName、LastName

第三种 用下划线“_”来连接所有的单词,比如send_buf,这是python主推的命名方法,叫做snake-case。

驼峰法

驼峰法

注:python中的标识符是区分大小写的

大小写

3.4 常用的数据类型转换

函数 说明
int(x [,base ]) 将x转换为一个整数
long(x [,base ]) 将x转换为一个长整数
float(x ) 将x转换到一个浮点数
complex(real [,imag ]) 创建一个复数
str(x ) 将对象 x 转换为字符串
repr(x ) 将对象 x 转换为表达式字符串
eval(str ) 用来计算在字符串中的有效Python表达式,并返回一个对象
tuple(s ) 将序列 s 转换为一个元组
list(s ) 将序列 s 转换为一个列表
chr(x ) 将一个整数转换为一个字符
unichr(x ) 将一个整数转换为Unicode字符
ord(x ) 将一个字符转换为它的整数值
hex(x ) 将一个整数转换为一个十六进制字符串
oct(x ) 将一个整数转换为一个八进制字符串

举例

    a = '100' # 此时a的类型是一个字符串,里面存放了100这3个字符
    b = int(a) # 此时b的类型是整型,里面存放的是数字100
    print("a=%d"%b)

4 注释

4.1 单行注释

以#开头,#右边的所有东西当做说明,而不是真正要执行的程序,起辅助说明作用

    # 我是注释,可以在里写一些功能说明之类的哦
    print('hello world')

4.2 多行注释

    '''我是多行注释,可以写很多很多行的 哈哈哈。。。 哈哈哈。。。 '''

    ''' 我是多行注释,可以写很多很多行的 哈哈哈。。。 哈哈哈。。。 '''
 print('hello world')

4.3 编码

如果直接在程序中用到了中文,比如

    print('你好,我是AI浩')

如果直接运行输出,程序会出现编码的问题(这和平台有关)

解决的办法为:在程序的开头写入如下代码,这就是中文注释

    #coding=utf-8

修改之后的程序:

    #coding=utf-8
    print('你好,我是AI浩')

运行结果:

    你好

注意:在python的语法规范中推荐使用的方式:

# -*- coding:utf-8 -*-

5 输入输出

5.1 输入

input()接受表达式输入,并把表达式的结果赋值给等号左边的变量

a = input("请输入数字:")
print(a)
print(type(a))
b=input("请输入字符串:")
print(b)
print(type(b))
c= input("请输入数学表达式:")
print(c)
print(type(c))
d= input("请输入字符串表达式:")
print(d)
print(type(d))

运行结果:

请输入数字:123
123
<class 'str'>
请输入字符串:abc
abc
<class 'str'>
请输入数学表达式:123+456
123+456
<class 'str'>
请输入字符串表达式:sdsf,1223
sdsf,1223
<class 'str'>

5.2 输出

print() 将python的输入

    # 打印提示
    print('hello world')

5.2.1 格式化输出

如下代码:

age = 100
print("我今年%d岁"%age)
age += 1
print("我今年%d岁"%age)
age += 1
print("我今年%d岁"%age)

运行结果:

我今年100岁
我今年101岁
我今年102岁

在程序中,看到了%这样的操作符,这就是Python中格式化输出。

    age = 18
    name = "lifeifei"
    print("我的姓名是%s,年龄是%d"%(name,age))

常用的格式符号

格式符号 转换
%c 字符
%s 通过str() 字符串转换来格式化
%i 有符号十进制整数
%d 有符号十进制整数
%u 无符号十进制整数
%o 八进制整数
%x 十六进制整数(小写字母)
%X 十六进制整数(大写字母)
%e 索引符号(小写’e’)
%E 索引符号(大写“E”)
%f 浮点实数
%g %f和%e 的简写
%G %f和%E的简写

在python3.6版本中新增了f-string格式化方式,称之为字面量格式化字符串,是新的格式化字符串的语法。

例:

name = 'AI浩'
print(f'Hello { 
     name}')  # 替换变量

运行结果:

Hello AI浩

f-string 格式化字符串以 f 开头,后面跟着字符串,字符串中的表达式用大括号 {} 包起来,它会将变量或表达式计算后的值替换进去,实例如下:

dicPeople= { 
   'name': 'AIHao', 'Age': 111}
print(f'{ 
     dicPeople["name"]}: { 
     dicPeople["Age"]}')

运行结果:

AIHao: 111

用了这种方式明显更简单了,不用再去判断使用 %s,还是 %d。

在 Python 3.8 的版本中可以使用 = 符号来拼接运算表达式与结果:

x=10
print(f"{ 
     x+10=}")

运行结果:

x+10=20

5.2.2换行输出

在输出的时候,如果有\n那么,此时\n后的内容会在另外一行显示

    print("1234567890-------") # 会在一行显示
    print("1234567890\n-------") # 一行显示1234567890,另外一行显示-------

运行结果:

1234567890-------
1234567890
-------

6 运算符

python支持以下几种运算符

  • 算术运算符

下面以a=10 ,b=20为例进行计算

运算符 描述 实例
+ 两个对象相加 a + b 输出结果 30
得到负数或是一个数减去另一个数 a – b 输出结果 -10
* 两个数相乘或是返回一个被重复若干次的字符串 a * b 输出结果 200
/ x除以y b / a 输出结果 2
// 取整除 返回商的整数部分 9//2 输出结果 4 , 9.0//2.0 输出结果 4.0
% 取余 返回除法的余数 b % a 输出结果 0
** 返回x的y次幂 a**b 为10的20次方, 输出结果 100000000000000000000
>>> 9/2.0
4.5
>>> 9//2.0
4.0
  • 赋值运算符
运算符 描述 实例
= 赋值运算符 把=号右边的结果给左边的变量 num=1+2*3 结果num的值为7
>>> a, b = 1, 2
>>> a
1
>>> b
2
  • 复合赋值运算符
运算符 描述 实例
+= 加法赋值运算符 c += a 等效于 c = c + a
-= 减法赋值运算符 c -= a 等效于 c = c – a
*= 乘法赋值运算符 c *= a 等效于 c = c * a
/= 除法赋值运算符 c /= a 等效于 c = c / a
%= 取模赋值运算符 c %= a 等效于 c = c % a
**= 幂赋值运算符 c **= a 等效于 c = c ** a
//= 取整除赋值运算符 c //= a 等效于 c = c // a
  • 比较(即关系)运算符

python中的比较运算符如下表

运算符 描述 示例
== 检查两个操作数的值是否相等,如果是则条件变为真。 如a=3,b=3则(a == b) 为 true.
!= 检查两个操作数的值是否相等,如果值不相等,则条件变为真。 如a=1,b=3则(a != b) 为 true.
<> 检查两个操作数的值是否相等,如果值不相等,则条件变为真。 如a=1,b=3则(a <> b) 为 true。这个类似于 != 运算符
> 检查左操作数的值是否大于右操作数的值,如果是,则条件成立。 如a=7,b=3则(a > b) 为 true.
< 检查左操作数的值是否小于右操作数的值,如果是,则条件成立。 如a=7,b=3则(a < b) 为 false.
>= 检查左操作数的值是否大于或等于右操作数的值,如果是,则条件成立。 如a=3,b=3则(a >= b) 为 true.
<= 检查左操作数的值是否小于或等于右操作数的值,如果是,则条件成立。 如a=3,b=3则(a <= b) 为 true.
  • 逻辑运算符
运算符 逻辑表达式 描述 实例
and x and y 布尔”与” – 如果 x 为 False,x and y 返回 False,否则它返回 y 的计算值。 (a and b) 返回 20。
or x or y 布尔”或” – 如果 x 是 True,它返回 True,否则它返回 y 的计算值。 (a or b) 返回 10。
not not x 布尔”非” – 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。 not(a and b) 返回 False

7 判断语句

7.1 if语句

  • if语句是用来进行判断的,其使用格式如下:
    if 要判断的条件:
        条件成立时,要做的事情

例1:

age = 30
print("------if判断开始------")
if age >= 18:
    print ("我已经成年了,我可以约妹子了!!!")
print ("------if判断结束------")

运行结果:

------if判断开始------
我已经成年了,我可以约妹子了!!!
------if判断结束------

例2:

age = 16
print("------if判断开始------")
if age >= 18:
    print ("我已经成年了,我可以约妹子了!!!")
print ("------if判断结束------")

运行结果:

    ------if判断开始------
    ------if判断结束------

**总结:**以上2个例子仅仅是age变量的值不一样,结果却不同;能够看得出if判断语句的作用:就是当满足一定条件时才会执行那块代码,否则就不执行那块代码

7.2 if-else语句

    if 条件:
        满足条件时要做的事情1
        满足条件时要做的事情2
        满足条件时要做的事情3
        ......
    else:
        不满足条件时要做的事情1
        不满足条件时要做的事情2
        不满足条件时要做的事情3
        ......

例:

age = 30
print("------if判断开始------")
if age >= 18:
    print ("我已经成年了,我可以约妹子了!!!")
else:
    print("还没有成年,不能约妹子!!!")
print ("------if判断结束------")

结果1:年龄大于18

------if判断开始------
我已经成年了,我可以约妹子了!!!
------if判断结束------

结果2:年龄小于18

------if判断开始------
还没有成年,不能约妹子!!!
------if判断结束------

7.3 elif

elif的使用格式如下:

    if xxx1:
        事情1
    elif xxx2:
        事情2
    elif xxx3:
        事情3

说明:

  • 当xxx1满足时,执行事情1,然后整个if结束
  • 当xxx1不满足时,那么判断xxx2,如果xxx2满足,则执行事情2,然后整个if结束
  • 当xxx1不满足时,xxx2也不满足,如果xxx3满足,则执行事情3,然后整个if结束

例:

score = 66
if 90 <= score <= 100:
    print('本次考试,等级为A')
elif 80 <= score < 90:
    print('本次考试,等级为B')
elif 70 <= score < 80:
    print('本次考试,等级为C')
elif 60 <= score < 70:
    print('本次考试,等级为D')
elif 0 <= score < 60:
    print('本次考试,等级为E')

可以和else一起使用

   if 性别为男性:
       输出男性的特征
       ...
   elif 性别为女性:
       输出女性的特征
       ...
   else:
       第三种性别的特征
       ...

说明:

  • 当 “性别为男性” 满足时,执行 “输出男性的特征”的相关代码

  • 当 “性别为男性” 不满足时,如果 “性别为女性”满足,则执行 “输出女性的特征”的相关代码

  • 当 “性别为男性” 不满足,“性别为女性”也不满足,那么久默认执行else后面的代码,即 “第三种性别的特征”相关代码

  • elif必须和if一起使用,否则出错

7.4 if嵌套

if嵌套的格式

    if 条件1:

        满足条件1 做的事情1
        满足条件1 做的事情2
        ......

        if 条件2:
            满足条件2 做的事情1
            满足条件2 做的事情2
            ......
  • 说明
    • 外层的if判断,也可以是if-else
    • 内层的if判断,也可以是if-else
    • 根据实际开发的情况,进行选择

if嵌套的应用举例:

age = 16
girl = False
if age >= 18:
    print("我已经成年了,我可以约妹子了!!!")
    if girl:
        print("有女朋友,去约会!!")
    else:
        print("没有女朋友,约个屁啊!!")
else:
    print("还没有成年,不能约妹子!!!")
    if girl:
        print("还没有成年,这是早恋啊!!")
    else:
        print("听话的好孩子!!!")

结果1:age= 30;girl= True

我已经成年了,我可以约妹子了!!!
有女朋友,去约会!!

结果2:age= 30;girl= False

我已经成年了,我可以约妹子了!!!
没有女朋友,约个屁啊!!

结果3:age= 16;girl= False

还没有成年,不能约妹子!!!
听话的好孩子!!!

结果4:age= 16;girl= True

还没有成年,不能约妹子!!!
还没有成年,这是早恋啊!!

8 循环、break和continue

8.1 while循环

    while 条件:
        条件满足时,做的事情1
        条件满足时,做的事情2
        条件满足时,做的事情3
        ...(省略)...

例:

i = 0
while i < 5:
    print("当前是第%d次执行循环" % (i + 1))
    print("i=%d" % i)
    i += 1

结果:

    当前是第1次执行循环
    i=0
    当前是第2次执行循环
    i=1
    当前是第3次执行循环
    i=2
    当前是第4次执行循环
    i=3
    当前是第5次执行循环
    i=4

案例1. 计算1~100的累积和(包含1和100)

参考代码如下:

# encoding=utf-8
i = 1
sum = 0
while i <= 100:
    sum = sum + i
    i += 1
print("1~100的累积和为:%d" % sum)

运行结果:1~100的累积和为:5050

案例2. 计算1~100之间偶数的累积和(包含1和100)

# encoding=utf-8
i = 1
sum = 0
while i <= 100:
    if i % 2 == 0:
        sum = sum + i
    i += 1

print("1~100的偶数累积和为:%d" % sum)

运行结果:1~100的偶数累积和为:2550

8.2 while循环嵌套

类似if的嵌套,while嵌套就是:while里面还有while

格式如下

    while 条件1:

        条件1满足时,做的事情1
        条件1满足时,做的事情2   
        ......
        while 条件2:
            条件2满足时,做的事情1
            条件2满足时,做的事情2
            条件2满足时,做的事情3
            ...(省略)...

案例1 要求:打印如下图形:

    *
    * *
    * * *
    * * * *
    * * * * *

参考代码:

    i = 1
    while i<=5:
        j = 1
        while j<=i:
            print("* ",end='')
            j+=1
        print("\n")
        i+=1

案例2 :九九乘法表

    i = 1
    while i<=9:
        j=1
        while j<=i:
            print("%d*%d=%-2d "%(j,i,i*j),end='')
            j+=1
        print('\n')
        i+=1

运行结果:

1*1=1  

1*2=2  2*2=4  

1*3=3  2*3=6  3*3=9  

1*4=4  2*4=8  3*4=12 4*4=16 

1*5=5  2*5=10 3*5=15 4*5=20 5*5=25 

1*6=6  2*6=12 3*6=18 4*6=24 5*6=30 6*6=36 

1*7=7  2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49 

1*8=8  2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64 

1*9=9  2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81 

8.3 for循环

像while循环一样,for可以完成循环的功能。

在Python中 for循环可以遍历任何序列的项目,如一个列表或者一个字符串等。

格式如下:

    for 临时变量 in 列表或者字符串等:
        循环满足条件时执行的代码
    else:
        循环不满足条件时执行的代码

例1:

name = 'AIHao'
for x in name:
    print(x)

运行结果如下:

A
I
H
a
o

案例2:

name = ''
for x in name:
    print(x)
else:
    print("没有数据")

运行结果如下:

没有数据

8.5 break

1) for循环

  name = 'AIHao'
  for x in name:
      print('----')
      if x == 'a': 
          break
      print(x)

运行结果:

----
A
----
I
----
H
----

2)while循环

  i = 0
  while i<10:
      i = i+1
      print('----')
      if i==5:
          break
      print(i)

运行结果:

----
1
----
2
----
3
----
4
----

**总结:**break的作用:用来结束整个循环

8.6 continue

1) for循环

  name = 'AIHao'
  for x in name:
      print('----')
      if x == 'a': 
          continue
      print(x)

运行结果:

----
A
----
I
----
H
----
----
o

2) while循环

  i = 0
  while i<10:
      i = i+1
      print('----')
      if i==5:
          continue
      print(i)

运行结果:

----
1
----
2
----
3
----
4
----
----
6
----
7
----
8
----
9
----
10

**总结:**continue的作用:用来结束本次循环,紧接着执行下一次的循环

注:

  • break/continue只能用在循环中,除此以外不能单独使用
  • break/continue在嵌套循环中,只对最近的一层循环起作用

9 字符串、列表、元组、字典

9.1 字符串

1)使用单引号或双引号定义的数据就是字符串,例:

a='hello'
或者
a="hello"

2)字符串的输出使用print()

name = 'AI浩'
position = '算法工程师'
address = '北京石景山'
print('--------------------------------------------------')
print("姓名:%s" % name)
print("职位:%s" % position)
print("公司地址:%s" % address)
print('--------------------------------------------------')

运行结果:

--------------------------------------------------
姓名:AI浩
职位:算法工程师
公司地址:北京石景山
--------------------------------------------------

3)字符串输入

userName = input('请输入用户名:')
print("用户名为:%s" % userName)
password = input('请输入密码:')
print("密码为:%s" % password)

结果:(根据输入的不同结果也不同)

请输入用户名:dfsdfsd
用户名为:dfsdfsd
请输入密码:qqq
密码为:qqq

4)下标和切片

下标的使用

name = 'abcdef'
print(name[0])
print(name[1])
print(name[2])

运行结果:

a
b
c

切片:

切片是指对操作的对象截取其中一部分的操作。字符串、列表、元组都支持切片操作。

切片的语法:[起始:结束:步长]

注意:选取的区间属于左闭右开型,即从”起始”位开始,到”结束”位的前一位结束(不包含结束位本身)。

name = 'abcdef'
print(name[0:3])  # 取 下标0~2 的字符

运行结果:

abc

9.2 字符串常见操作

如有字符串mystr = 'hello world AIHao and CSDN',以下是常见的操作

9.2.1 find

检测 str 是否包含在 mystr中,如果是返回开始的索引值,否则返回-1,语法:

mystr.find(str, start=0, end=len(mystr))

例:

mystr = 'hello world AIHao and CSDN'
print(mystr.find("AIHao", 0, len(mystr)))

运行结果:

12

9.2.2 index

跟find()方法一样,只不过如果str不在 mystr中会报一个异常,语法:

mystr.index(str, start=0, end=len(mystr)) 

例:

mystr = 'hello world AIHao and CSDN'
print(mystr.index("Hao", 0, len(mystr)))

运行结果:

14

9.2.3 count

返回 str在start和end之间 在 mystr里面出现的次数,语法:

mystr.count(str, start=0, end=len(mystr))

例:

mystr = 'hello world AIHao and CSDN'
print(mystr.count("a", 0, len(mystr)))

运行结果:2

9.2.4 replace

把 mystr 中的 str1 替换成 str2,如果 count 指定,则替换不超过 count 次,语法:

mystr.replace(str1, str2,  mystr.count(str1))

例:

mystr = 'hello world AIHao and CSDN'
newStr=mystr.replace("a","ee",mystr.count("a"))
print(newStr)

运行结果:

hello world AIHeeo eend CSDN

9.2.5 split

以 str 为分隔符切片 mystr,如果 maxsplit有指定值,则仅分隔 maxsplit 个子字符串,语法:

mystr.split(str=" ", 2)    

例:

mystr = 'hello world AIHao and CSDN'
newStr=mystr.split(" ",2)
print(newStr)

运行结果:

['hello', 'world', 'AIHao and CSDN']
mystr = 'hello world AIHao and CSDN'
newStr=mystr.split(" ")
print(newStr)

运行结果:

['hello', 'world', 'AIHao', 'and', 'CSDN']

9.2.6 capitalize

把字符串的第一个字符大写,其他的变成小写,语法:

mystr.capitalize()

例:

mystr = 'hello world AIHao and CSDN'
print(mystr.capitalize())

运行结果:

Hello world aihao and csdn

9.2.7 title

把字符串的每个单词首字母大写,其他的改成小写,例:

mystr = 'hello world AIHao and CSDN'
print(mystr.title())

运行结果:

Hello World Aihao And Csdn

9.2.8 startswith

检查字符串是否是以 obj 开头, 是则返回 True,否则返回 False

mystr.startswith(obj)

例:

mystr = 'hello world AIHao and CSDN'
ss='hello'
print(mystr.startswith(ss))

运行结果:True

9.2.9 endswith

检查字符串是否以obj结束,如果是返回True,否则返回 False,用法同上

mystr.endswith(obj)

9.2.10 lower

转换 mystr 中所有大写字符为小写

mystr.lower()        

例:

mystr = 'hello world AIHao and CSDN'
print(mystr.lower())

运行结果:

hello world aihao and csdn

9.2.11 upper

转换 mystr 中的小写字母为大写,用法同上。

mystr.upper()    

9.2.12 ljust

返回一个原字符串左对齐,并使用空格填充至长度 width 的新字符串

mystr.ljust(width) 

例:

mystr = "hello world AIHao and CSDN"
print(mystr.ljust(50))

运行结果:

hello world AIHao and CSDN    

9.2.13 rjust

返回一个原字符串右对齐,并使用空格填充至长度 width 的新字符串

mystr.rjust(width)    

例:

mystr = "hello world AIHao and CSDN"
print(mystr.rjust(30))

运行结果:

    hello world AIHao and CSDN

9.2.14 center

返回一个原字符串居中,并使用空格填充至长度 width 的新字符串,用法同上。

mystr.center(width)   

9.2.15 lstrip

删除 mystr 左边的空白字符

mystr.lstrip()

9.2.16 rstrip

删除 mystr 字符串末尾的空白字符

mystr.rstrip()    

9.2.17 strip

删除mystr字符串两端的空白字符

>>> a = "\n\t itcast \t\n"
>>> a.strip()
'itcast'

9.2.18 rfind

类似于 find()函数,不过是从右边开始查找.

mystr.rfind(str, start=0,end=len(mystr) )

9.2.19 rindex

类似于 index(),不过是从右边开始.

mystr.rindex( str, start=0,end=len(mystr))

9.2.20 partition

把mystr以str分割成三部分,str前,str和str后

mystr.partition(str)

9.2.21 rpartition

类似于 partition()函数,不过是从右边开始.

mystr.rpartition(str)

9.2.22 splitlines

按照行分隔,返回一个包含各行作为元素的列表

mystr.splitlines()  

9.2.23 isalpha

如果 mystr 所有字符都是字母 则返回 True,否则返回 False

mystr.isalpha()  

9.2.24 isdigit

如果 mystr 只包含数字则返回 True 否则返回 False.

mystr.isdigit() 

9.2.25 isalnum

如果 mystr 所有字符都是字母或数字则返回 True,否则返回 False

mystr.isalnum()  

9.2.26 isspace

如果 mystr 中只包含空格,则返回 True,否则返回 False.

mystr.isspace()   

9.2.27 join

将mystr 中每个字符后面插入str,构造出一个新的字符串

mystr.join(str)

例:

str_new=" ".join(_my)
print(str_new)
_new1='-'.join(_my)
print(_new1)

运行结果:

my name is AIhao
my-name-is-AIhao

9.3 列表List

列表list用[,…]表示,格式如下:

  namesList = ['xiaoWang','xiaoZhang','xiaoHua']

9.3.1 for循环遍历list

1)例:

namesList = ['xiaoWang', 'xiaoZhang', 'xiaoHua']
for name in namesList:
    print(name)

结果:

    xiaoWang
    xiaoZhang
    xiaoHua

列表推导式

1)普通方式

a = [x for x in range(4)]
print(a)
b = [x for x in range(3, 4)]
print(b)
c = [x for x in range(3, 19)]
print(c)
d = [x for x in range(3, 19, 2)]
print(d)

运行结果:

[0, 1, 2, 3]
[3]
[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
[3, 5, 7, 9, 11, 13, 15, 17]

2)在循环的过程中加入if判断

a = [x for x in range(3, 15) if x % 2 == 0]
print(a)
b = [x for x in range(3, 15) if x % 2 != 0]
print(b)
b = [x for x in range(3, 15)]
print(b)

运行结果:

[4, 6, 8, 10, 12, 14]
[3, 5, 7, 9, 11, 13]
[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

3)多个for循环

a = [(x, y) for x in range(3, 5) for y in range(3)]
print(a)
b = [(x, y, z) for x in range(3, 5) for y in range(3) for z in range(4, 6)]
print(b)

运行结果:

[(3, 0), (3, 1), (3, 2), (4, 0), (4, 1), (4, 2)]
[(3, 0, 4), (3, 0, 5), (3, 1, 4), (3, 1, 5), (3, 2, 4), (3, 2, 5), (4, 0, 4), (4, 0, 5), (4, 1, 4), (4, 1, 5), (4, 2, 4), (4, 2, 5)]

9.3.2 while循环遍历list

namesList = ['xiaoWang', 'xiaoZhang', 'xiaoHua']
length = len(namesList)
i = 0
while i < length:
    print(namesList[i])
    i += 1

运行结果:

xiaoWang
xiaoZhang
xiaoHua

9.4 列表的常用操作

列表中存放的数据是可以进行修改的,比如”增”、“删”、“改””

9.4.1 添加元素(“增”append, extend, insert)

append:通过append可以向列表添加元素

例:

    #定义变量A,默认有3个元素
    A = ['xiaoWang','xiaoZhang','xiaoHua']
    print("-----添加之前,列表A的数据-----")
    for tempName in A:
        print(tempName)
    #提示、并添加元素
    temp = input('请输入姓名:')
    A.append(temp)
    print("-----添加之后,列表A的数据-----")
    for tempName in A:
        print(tempName)

结果:

-----添加之前,列表A的数据-----
xiaoWang
xiaoZhang
xiaoHua
请输入姓名:xiaohei
-----添加之后,列表A的数据-----
xiaoWang
xiaoZhang
xiaoHua
xiaohei

extend:通过extend可以将另一个集合中的元素逐一添加到列表中

a = [1, 2]
b = [3, 4]
a.append(b)
print(a)
a.extend(b)
print(a)

结果:

[1, 2, [3, 4]]
[1, 2, [3, 4], 3, 4]

insert :insert(index, object) 在指定位置index前插入元素object

a = [0, 1, 2]
a.insert(1, 3)
print(a)

结果:

[0, 3, 1, 2]

9.4.2修改元素(“改”)

修改元素的时候,要通过下标来确定要修改的是哪个元素,然后才能进行修改,例:

    #定义变量A,默认有3个元素
    A = ['xiaoWang','xiaoZhang','xiaoHua']

    print("-----修改之前,列表A的数据-----")
    for tempName in A:
        print(tempName)
    #修改元素
    A[1] = 'xiaoLu'
    print("-----修改之后,列表A的数据-----")
    for tempName in A:
        print(tempName)

结果:

    -----修改之前,列表A的数据-----
    xiaoWang
    xiaoZhang
    xiaoHua
    -----修改之后,列表A的数据-----
    xiaoWang
    xiaoLu
    xiaoHua

9.4.3 查找元素(“查”in, not in, index, count)

所谓的查找,就是看看指定的元素是否存在

in, not in

python中查找的常用方法为:

  • in(存在),如果存在那么结果为true,否则为false
  • not in(不存在),如果不存在那么结果为true,否则false

demo

    #待查找的列表
    nameList = ['xiaoWang','xiaoZhang','xiaoHua']
    #获取用户要查找的名字
    findName = input('请输入要查找的姓名:')
    #查找是否存在
    if findName in nameList:
        print('在字典中找到了相同的名字')
    else:
        print('没有找到')

说明:

in的方法只要会用了,那么not in也是同样的用法,只不过not in判断的是不存在

index, count

index和count与字符串中的用法相同

a = ['a', 'b', 'c', 'a', 'b']
print(a.index('a', 1, 4))
print(a.count('b'))
print(a.count('d'))
print(a.index('a', 1, 3)) # 注意是左闭右开区间

运行结果:

Traceback (most recent call last):
  File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 5, in <module>
    print(a.index('a', 1, 3)) # 注意是左闭右开区间
ValueError: 'a' is not in list
3
2
0

9.4.4 删除元素(“删”del, pop, remove)

类比现实生活中,如果某位同学调班了,那么就应该把这个条走后的学生的姓名删除掉;在开发中经常会用到删除这种功能。

列表元素的常用删除方法有:

  • del:根据下标进行删除
  • pop:删除最后一个元素
  • remove:根据元素的值进行删除

demo:(del)

    movieName = ['加勒比海盗','骇客帝国','第一滴血','指环王','霍比特人','速度与激情']
    print('------删除之前------')
    for tempName in movieName:
        print(tempName)
    del movieName[2]
    print('------删除之后------')
    for tempName in movieName:
        print(tempName)

结果:

    ------删除之前------
    加勒比海盗
    骇客帝国
    第一滴血
    指环王
    霍比特人
    速度与激情
    ------删除之后------
    加勒比海盗
    骇客帝国
    指环王
    霍比特人
    速度与激情

例:(pop)

    movieName = ['加勒比海盗','骇客帝国','第一滴血','指环王','霍比特人','速度与激情']

    print('------删除之前------')
    for tempName in movieName:
        print(tempName)

    movieName.pop()

    print('------删除之后------')
    for tempName in movieName:
        print(tempName)

结果:

    ------删除之前------
    加勒比海盗
    骇客帝国
    第一滴血
    指环王
    霍比特人
    速度与激情
    ------删除之后------
    加勒比海盗
    骇客帝国
    第一滴血
    指环王
    霍比特人

demo:(remove)

    movieName = ['加勒比海盗','骇客帝国','第一滴血','指环王','霍比特人','速度与激情']

    print('------删除之前------')
    for tempName in movieName:
        print(tempName)

    movieName.remove('指环王')

    print('------删除之后------')
    for tempName in movieName:
        print(tempName)

结果:

    ------删除之前------
    加勒比海盗
    骇客帝国
    第一滴血
    指环王
    霍比特人
    速度与激情
    ------删除之后------
    加勒比海盗
    骇客帝国
    第一滴血
    霍比特人
    速度与激情

9.4.5 排序(sort, reverse)

sort方法是将list按特定顺序重新排列,默认为由小到大,参数reverse=True可改为倒序,由大到小。

reverse方法是将list逆置。

>>> a = [1, 4, 2, 3]
>>> a
[1, 4, 2, 3]
>>> a.reverse()
>>> a
[3, 2, 4, 1]
>>> a.sort()
>>> a
[1, 2, 3, 4]
>>> a.sort(reverse=True)
>>> a
[4, 3, 2, 1]

9.4.6 列表的嵌套

类似while循环的嵌套,列表也是支持嵌套的

一个列表中的元素又是一个列表,那么这就是列表的嵌套

    schoolNames = [['北京大学','清华大学'],
                    ['南开大学','天津大学','天津师范大学'],
                    ['山东大学','中国海洋大学']]

案例:一个学校,有3个办公室,现在有8位老师等待工位的分配,请编写程序,完成随机的分配

#encoding=utf-8
import random
# 定义一个列表用来保存3个办公室
offices = [[],[],[]]
# 定义一个列表用来存储8位老师的名字
names = ['A','B','C','D','E','F','G','H']
i = 0
for name in names:
    index = random.randint(0,2)    
    offices[index].append(name)
i = 1
for tempNames in offices:
    print('办公室%d的人数为:%d'%(i,len(tempNames)))
    i+=1
    for name in tempNames:
        print("%s"%name,end='')
    print("\n")
    print("-"*20)

运行结果如下:

办公室1的人数为:3
BFG

--------------------
办公室2的人数为:5
ACDEH

--------------------
办公室3的人数为:0


--------------------

9.5 元组

Python的元组与列表类似,不同之处在于元组的元素不能修改。元组使用小括号(),列表使用方括号[],例:

aTuple = ('et',77,99.9)
print(aTuple)

运行结果:(‘et’, 77, 99.9)

9.5.1 访问元组

aTuple = ('et',77,99.9)
print(aTuple[0])
print(aTuple[1])

运行结果:

et
77

9.5.2 修改元组

aTuple = ('et',77,99.9)
aTuple[0]=111

运行结果:

Traceback (most recent call last):
  File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 2, in <module>
    aTuple[0]=111
TypeError: 'tuple' object does not support item assignment

从上面的运行结果可以得出: python中不允许修改元组的数据,包括不能删除其中的元素。

9.5.3 元组的内置函数count, index

index和count与字符串和列表中的用法相同

>>> a = ('a', 'b', 'c', 'a', 'b')
>>> a.index('a', 1, 3) # 注意是左闭右开区间
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: tuple.index(x): x not in tuple
>>> a.index('a', 1, 4)
3
>>> a.count('b')
2
>>> a.count('d')
0

9.6 字典

字典是一种可变容器模型,且可存储任意类型对象。

字典的每个键值 key=>value 对用冒号 : 分割,每个对之间用逗号(,)分割,整个字典包括在花括号 {} 中 ,格式如下所示:

d = {key1 : value1, key2 : value2, key3 : value3 }

新建 Microsoft Visio 绘图

例:

   info = { 
   'name':'AI浩', 'id':100, 'sex':'f', 'address':'中国北京'}

9.6.1 根据键访问值

    info = { 
   'name':'AI浩', 'id':100, 'sex':'f', 'address':'中国北京'}
    print(info['name'])
    print(info['address'])

结果:

    AI浩
    中国北京

若访问不存在的键,则会报错:

info = { 
   'name': 'AI浩', 'id': 100, 'sex': 'f', 'address': '中国北京'}
print(info['age'])

运行结果:

Traceback (most recent call last):
  File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 2, in <module>
    print(info['age'])
KeyError: 'age'

在我们不确定字典中是否存在某个键而又想获取其值时,可以使用get方法,还可以设置默认值:

>>> age = info.get('age')
>>> age #'age'键不存在,所以age为None
>>> type(age)
<type 'NoneType'>
>>> age = info.get('age', 18) # 若info中不存在'age'这个键,就返回默认值18
>>> age
18

9.7 字典的常用操作

9.7.1 修改元素

字典的每个元素中的数据是可以修改的,只要通过key找到,即可修改,例:

info = { 
   'name': 'AI浩', 'id': 100, 'sex': 'f', 'address': '中国北京'}
info['id'] = 150
print('修改之后的id为:%d' % info['id'])

结果:修改之后的id为:150

9.7.2 添加元素

使用 变量名[‘键’] = 数据 时,这个“键”在字典中,不存在,那么就会新增这个元素,例:

info = { 
   'name': 'AI浩',  'sex': 'f', 'address': '中国北京'}
newId = input('请输入新的ID')
info['id'] = int(newId)

print('添加之后的id为:%d' % info['id'])

结果:

请输入新的ID123
添加之后的id为:123

9.7.3 删除元素

对字典进行删除操作,有一下几种:

  • del
  • clear()

例1:del删除指定的元素

info = { 
   'name': 'AI浩',  'sex': 'f', 'address': '中国北京'}
print('删除前,%s' % info['name'])
del info['name']
print('删除后,%s' % info['name'])

结果:

删除前,AI浩
Traceback (most recent call last):
  File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 4, in <module>
    print('删除后,%s' % info['name'])
KeyError: 'name'

例2:del删除整个字典

info = { 
   'name': 'AI浩',  'sex': 'f', 'address': '中国北京'}
print('删除前,%s' % info)
del info
print('删除后,%s' % info)

运行结果:

Traceback (most recent call last):
  File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 4, in <module>
    print('删除后,%s' % info)
NameError: name 'info' is not defined
删除前,{'name': 'AI浩', 'sex': 'f', 'address': '中国北京'}

例3:clear清空整个字典

info = { 
   'name': 'AI浩',  'sex': 'f', 'address': '中国北京'}
print('清空前,%s' % info)
info.clear()
print('清空后,%s' % info)

运行结果:

清空前,{'name': 'AI浩', 'sex': 'f', 'address': '中国北京'}
清空后,{}

9.7.4 字典的长度

使用len()方法,求字典的长度,例:

info = { 
   'name': 'AI浩',  'sex': 'f', 'address': '中国北京'}
print('字典的长度:%d' % len(info))

运行结果:字典的长度:3

9.7.5 找出字典中的所有key

keys返回一个包含字典所有KEY的视图对象,例:

info = { 
   'name': 'AI浩',  'sex': 'f', 'address': '中国北京'}
dicKeys=info.keys()
print(dicKeys)
print(dicKeys[0])

运行结果:

Traceback (most recent call last):
  File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 4, in <module>
    print(dicKeys[0])
TypeError: 'dict_keys' object is not subscriptable
dict_keys(['name', 'sex', 'address'])

注:返回的是个视图对象,不是list,很多资料说list是不对的,但是可以转为list,例:

info = { 
   'name': 'AI浩',  'sex': 'f', 'address': '中国北京'}
dicKeys=list(info.keys())
print(dicKeys)
print(dicKeys[0])

运行结果:

['name', 'sex', 'address']
name

9.7.6 找出字典所有的value

属性values返回一个包含字典所有value的视图列表,用法同上,例:

info = { 
   'name': 'AI浩',  'sex': 'f', 'address': '中国北京'}
dicvalues=list(info.values())
print(dicvalues)
print(dicvalues[0])

运行结果:

['AI浩', 'f', '中国北京']
AI浩

9.7.7 找出字典的(键,值)

属性items,返回一个包含所有(键,值)元祖的列表

info = { 
   'name': 'AI浩',  'sex': 'f', 'address': '中国北京'}
dicItems=info.items()
print(dicItems)

运行结果:

dict_items([('name', 'AI浩'), ('sex', 'f'), ('address', '中国北京')])

9.7.8 判断key是否存在

“key in dict”如果键在字典dict里返回true,否则返回false,例:

info = { 
   'name': 'AI浩',  'sex': 'f', 'address': '中国北京'}
ss='name' in info
print(ss)

运行结果:True

9.7.9 遍历字典的几种方式

1) 遍历字典的key(键)

info = { 
   'name': 'AI浩',  'sex': 'f', 'address': '中国北京'}
for key in info.keys():
    print(key)

运行结果:

name
sex
address

2) 遍历字典的value(值)

info = { 
   'name': 'AI浩',  'sex': 'f', 'address': '中国北京'}
for key in info.values():
    print(key)

运行结果:

AI浩
f
中国北京

3) 遍历字典的项(元素)

info = { 
   'name': 'AI浩',  'sex': 'f', 'address': '中国北京'}
for item in info.items():
    print(item)

运行结果:

('name', 'AI浩')
('sex', 'f')
('address', '中国北京')

4) 遍历字典的key-value(键值对)

info = { 
   'name': 'AI浩', 'sex': 'f', 'address': '中国北京'}
for key, value in info.items():
    print("key=%s,value=%s" % (key, value))

运行结果:

key=name,value=AI浩
key=sex,value=f
key=address,value=中国北京

10 函数

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。

函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。

10.1 定义函数

语法:

def 函数名(参数列表):
    函数体

例:

# 定义一个函数,能够完成打印信息的功能
def printInfo():
    print ('------------------------------------')
    print (' 人生苦短,我用Python')
    print ('------------------------------------')

定义函数的规则:

  • 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()
  • 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
  • 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  • 函数内容以冒号 : 起始,并且缩进。
  • return [表达式] 结束函数,选择性地返回一个值给调用方,不带表达式的 return 相当于返回 None。

10.2 调用函数

定义了函数之后,就相当于有了一个具有某些功能的代码,想要让这些代码能够执行,需要调用它

调用函数很简单的,通过 函数名() 即可完成调用,例:

    # 定义完函数后,函数是不会自动执行的,需要调用它才可以
    printInfo()

10.3 函数的注释

在函数定义下面使用'''进行注释,使用help函数可以查看函数的注释。

def test(a1, a2):
    ''' 用来完成对2个数求和" :param a1:第一个参数 :param a2:第二个参数 :return:无 '''
    print("%d" % (a1 + a2))


print(help(test))

运行结果:

test(a1, a2)
    用来完成对2个数求和"
    :param a1:第一个参数
    :param a2:第二个参数
    :return:无

None

10.4 函数的参数

10.4.1 参数的定义

​ 参数分形参、实参

形参:函数定义时括号内的参数

实参:函数调用时括号内的参数

形参相当于变量,实参相当于变量的值。

def add2num(a, b):
    c = a + b
    print(c)
    
add2num(11, 22)  # 调用带有参数的函数时,需要在小括号中,传递数据

a,b为形参;11,12为实参

形参:

只在被调用时,才分配内存单元。调用结束,立刻释放所分配的内存。

只在函数内部有效。

实参:

可以是:常量、变量、表达式、函数。

进行函数调用时,实参必须是确定的值。

10.4.2 参数的分类

Python的函数除了正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数,使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者的代码。

1)位置参数

位置形参:函数定义时,从左往右写的参数,比如上面的 a, b

位置实参:函数调用时,从左往右写的参数, 比如上面的 11,12

位置形参定义多少个,调用时位置实参必须写上多少个,多一个少一个都不行。

2)关键参数:

正常情况下,给函数传参数,要按顺序。如果不想按顺序,就用关键参数。

指定参数名的参数,就叫做关键参数。

函数调用时:func(a=1, b=2), 这种指定了参数名的参数,就是关键参数。

调用函数时,关键参数可以和位置参数一起用,但是关键参数必须在位置参数的后面。不然会报错。

例:

def add2num(a, b):
    c = a + b
    print(c)

#正确的调用方式
add2num(11, b=22)  # 调用带有参数的函数时,需要在小括号中,传递数据
add2num(a=11, b=22)
add2num(b=11, a=22)
# 错误的调用方式。
#add2num(22, a=22)
#add2num(b=11,22)

3)默认参数

函数定义时,默认参数必须在位置形参的后面。

函数调用时,指定参数名的参数,叫关键参数。

而在函数定义时,给参数名指定值的时候,这个参数叫做默认参数。

关键参数,和默认参数两个参数写法一样,区别在于:

关键参数是在函数调用时,指定实参的参数名,也可以说指定值的参数名。

默认参数是在函数定义时,指定参数名的值。

定义时,有默认参数的话,调用时,这个实参可以不写。如果实参不写的话,这个形参的参数值是他的默认值。

例:

def add2num(a, b=100):
    c = a + b
    print(c)


add2num(11, b=22)  # 调用带有参数的函数时,需要在小括号中,传递数据
add2num(11)

运行结果:

33
111

4 ) 动态参数:*args **kwargs

*args

针对函数定义时的*:

def func(a, b, *args):

pass

*args会接收函数调用时,传入的多余的位置实参。

*args 是一个元组

例子:

func(1, 2, 3, 4, 5, 6) 函数调用,因为函数定义时,*args前面有形参a, 形参b, *args就接收调用时多余的位置实参

a为1, b为2, *args 为: (3, 4, 5, 6),是一个元组。

针对函数调用时的 *:

func(1, 2, *[1, 2, 3, 4]) == func(1, 2, 1, 2, 3, 4)

函数调用时有*, 就应该立马将*后面的列表,元组,字符串之类的迭代器,打散成位置参数来看。

注意,如果 *后面是一个字典的话,那么打散成的位置参数就是字典的key

*可以看做是for循环。

形参中 *args 只能接收多余的位置实参,成为一个元组。不能接收关键实参。

例:

def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum
nums = [1, 2, 3]
# 调用方式1
print(calc(nums[0], nums[1], nums[2]))
#调用方式2
print(calc(*nums))

运行结果:

14
14

**kwargs:

针对函数定义时,站在形参的角度看 **:

接收多余的关键实参,成为一个字典dict。

字典的key是关键实参的变量名,字典的value是关键实参的值。

将字典交给**后面的变量名,这里是kwargs

站在实参的角度看 ** :

d = {‘x’:1, ‘y’:2, ‘z’:333}

func(**d) # 等价于func(x=1,y=2,z=333)

函数调用时,后面可以接一个字典,然后会把字典打散成关键参数的形式,也就是key=value的形式。

例:

def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)

#调用方式1
print(person('Michael', 30))
#调用方式2
print(person('Bob', 35, city='Beijing'))
#调用方式3
print(person('Adam', 45, gender='M', job='Engineer'))

运行结果:

name: Michael age: 30 other: {}
None
name: Bob age: 35 other: {'city': 'Beijing'}
None
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
None

5)混合参数时,参数顺序

函数定义时:

从左往右:位置形参 > *args > 默认参数 > **kwargs

位置形参 > 默认参数 > *args > **kwargs 也可以。

因为函数调用时给的实参满足了位置形参、默认参数之后,会把多余的位置实参给args。这样并不会报错。

但是 **kwargs 必须在 *args后面

默认形参必须在位置形参后面

​ 函数调用时:

从左到右:位置实参 > *args > 关键参数 > **kwargs

因为 * args 在函数调用时,会被打散成位置实参,而关键参数必须在位置实参的后面,否则会报错。SyntaxError: positional argument follows keyword argument

*args 必须在 **kwargs后面, 否则会报语法错误:SyntaxError: iterable argument unpacking follows keyword argument unpacking

总结

Python的函数具有非常灵活的参数形态,既可以实现简单的调用,又可以传入非常复杂的参数。

默认参数一定要用不可变对象,如果是可变对象,程序运行时会有逻辑错误!

要注意定义可变参数和关键字参数的语法:

*args是可变参数,args接收的是一个tuple;

**kw是关键字参数,kw接收的是一个dict。

以及调用函数时如何传入可变参数和关键字参数的语法:

可变参数既可以直接传入:func(1, 2, 3),又可以先组装list或tuple,再通过*args传入:func(*(1, 2, 3))

关键字参数既可以直接传入:func(a=1, b=2),又可以先组装dict,再通过**kw传入:func(**{'a': 1, 'b': 2})

使用*args**kw是Python的习惯写法,当然也可以用其他参数名,但最好使用习惯用法。

命名的关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。

定义命名的关键字参数在没有可变参数的情况下不要忘了写分隔符*,否则定义的将是位置参数。

10.4.3 参数的传递

Python参数传递采用的肯定是“传对象引用”的方式。这种方式相当于传值和传引用的一种综合。如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值--相当于通过“传引用”来传递对象。如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修改原始对象--相当于通过“传值’来传递对象。

例:

attrList = [0, 1, 2, 3, 4, 5]
print("改变前————————————————————————————————")
print(attrList)
def func(i, attrList):
    attrList[i] = attrList[i] * 10


print("改变后————————————————————————————————")
for i in range(3):
    func(i, attrList)
print(attrList)

运行结果:

改变前————————————————————————————————
[0, 1, 2, 3, 4, 5]
改变后————————————————————————————————
[0, 10, 20, 3, 4, 5]

可以看到,List发生了改变,如果传入不可变的元素呢?

a = 10
print("改变前————————————————————————————————")
print(a)


def func(c):
    print(id(c))
    c += 2
    print(id(c))
    print("func函数的c值:", c)

print(id(a))
func(a)
print("改变后————————————————————————————————")
print(a)
print(id(a))

运行结果:

改变前————————————————————————————————
10
140724141828160
140724141828160
140724141828224
func函数的c值: 12
改变后————————————————————————————————
10
140724141828160

将a变量作为参数传递给了func函数,传递了a的一个引用,把a的地址传递过去了,所以在函数内获取的变量c的地址跟变量a的地址是一样的,但是在函数内,对c进行赋值运算,c的值从10变成了12,实际上10和12所占的内存空间都还是存在的,赋值运算后,c指向12所在的内存。而a仍然指向10所在的内存,所以后面打印a,其值还是10.

10.5 函数的返回值

想要在函数中把结果返回给调用者,需要在函数中使用return,例:

def add2num(a, b):
    c = a + b
    return c


print(add2num(1, 2))

可以返回多个返回值,例:

def divid(a, b):
    shang = a // b
    yushu = a % b
    return shang, yushu


sh, yu = divid(5, 2)

10.6 局部变量与全局变量

  • 局部变量就是定义在一个函数体内部的变量

  • 全局变量是定义在外面的变量

    例:

    a = 1
    def f():
        b = 2
    

    其中a就是全局变量,而b是局部变量。局部变量只在函数体内部有效,出了函数体,外面是访问不到的,而全局变量则对下面的代码都有效。

    全局变量可以直接在函数体内容部使用的,你可以直接访问,但是注意的是,如果对于不可变类型的数据,如果在函数里面进行了赋值操作,则对外面的全局变量不产生影响,因为相当于新建了一个局部变量,只是名字和全局一样,而对于可变类型,如果使用赋值语句,同样对外部不产生影响,但是使用方法的话就会对外部产生影响。

    g_b = 3
    g_l1 = [1, 2]
    g_l2 = [1, 2, 3]
    
    
    def t1():
        g_b = 2
        g_l1 = []
        g_l2.append(7)
    
    
    t1()
    print(g_b, g_l1, g_l2)
    

    运行结果:3 [1, 2] [1, 2, 3, 7]

    global关键字
    上面说到,如果使用的是赋值语句,在函数内部相当于新建了一个变量,并且重新给了指向,但是有时候我们想把这个变量就是外部的那个全局变量,在赋值操作的时候,就是对全局变量给了重新的指向,这个时候可以通过global关键字表示我在函数里面的这个变量是使用的全局那个。使用方法如下:

g_b = 3


def t1():
    global g_b
    g_b = 2


t1()
print(g_b)

运行结果:2

这个时候你会发现全局变量g_b也重新指向了,这是因为global g_b表示指定了函数中的g_b就是外面的那个。

10.7 递归函数

如果一个函数在内部调用自身本身,这个函数就是递归函数。例:

def fact(n):
    if n == 1:
        return 1
    return n * fact(n - 1)


print(fact(5))

运行结果:120

可以根据函数定义看到计算过程如下:

===> fact(5)
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 * fact(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120

递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。

使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。可以试试fact(1000)。

解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。

尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。

上面的fact(n)函数由于return n * fact(n – 1)引入了乘法表达式,所以就不是尾递归了。要改成尾递归方式,需要多一点代码,主要是要把每一步的乘积传入到递归函数中,例:

def fact(n):
    return fact_iter(n, 1)

def fact_iter(num, product):
    if num == 1:
        return product
    return fact_iter(num - 1, num * product)

可以看到,return fact_iter(num – 1, num * product)仅返回递归函数本身,num – 1和num * product在函数调用前就会被计算,不影响函数调用。

fact(5)对应的fact_iter(5, 1)的调用如下:

===> fact_iter(5, 1)
===> fact_iter(4, 5)
===> fact_iter(3, 20)
===> fact_iter(2, 60)
===> fact_iter(1, 120)
===> 120

尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。

遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,所以,即使把上面的fact(n)函数改成尾递归方式,也会导致栈溢出。

小结

使用递归函数的优点是逻辑简单清晰,缺点是过深的调用会导致栈溢出。

针对尾递归优化的语言可以通过尾递归防止栈溢出。尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环。

Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。

10.8 匿名函数

10.8.1 定义

用lambda关键词能创建小型匿名函数。这种函数得名于省略了用def声明函数的标准步骤。

lambda函数的语法只包含一个语句,如下:

    lambda [arg1 [,arg2,.....argn]]:expression

如下实例:

sum = lambda a1, a2: a1 * a2

# 调用sum函数
print("Value of total : ", sum(10, 20))
print("Value of total : ", sum(20, 20))

以上实例输出结果:

Value of total :  200
Value of total :  400

Lambda函数能接收任何数量的参数但只能返回一个表达式的值

匿名函数不能直接调用print,因为lambda需要一个表达式

10.8.2 应用场合

例1:自己定义函数作为参数传递

def fun(a, b, opt):
    print("a =", a)
    print("b =", b)
    print("result =", opt(a, b))


fun(1, 2, lambda x, y: x + y)

运行结果:

a = 1
b = 2
result = 3

例2:作为内置函数的参数

想一想,下面的数据如何指定按age或name排序?

stus = [
    { 
   "name":"zhangsan", "age":18}, 
    { 
   "name":"lisi", "age":19}, 
    { 
   "name":"wangwu", "age":17}
]

按name排序:

stus.sort(key=lambda x: x['name'])
print(stus)

运行结果:

[{'name': 'lisi', 'age': 19}, {'name': 'wangwu', 'age': 17}, {'name': 'zhangsan', 'age': 18}]

按age排序:

stus.sort(key=lambda x: x['age'])
print(stus)

运行结果:

[{'name': 'wangwu', 'age': 17}, {'name': 'zhangsan', 'age': 18}, {'name': 'lisi', 'age': 19}]

11 文件操作

11.1 打开与关闭

11.1.1 打开文件

在python,使用open函数,可以打开一个已经存在的文件,或者创建一个新文件

open(文件名,访问模式)

示例如下:

    f = open('test.txt', 'w')

说明:

访问模式 说明
r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。
w 打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。
wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。
r+ 打开一个文件用于读写。文件指针将会放在文件的开头。
w+ 打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。
rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。
wb+ 以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。
ab+ 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

11.1.2 关闭文件

close( )

示例如下:

    # 新建一个文件,文件名为:test.txt
    f = open('test.txt', 'w')

    # 关闭这个文件
    f.close()

11.2 文件的读写

11.2.1 写数据(write)

使用write()可以完成向文件写入数据,例:

f = open('test.txt', 'w')
f.write('hello 大家好, 我是AI浩')
f.close()

运行现象:

image-20210831144255382

注意:

  • 如果文件不存在那么创建,如果存在那么就先清空,然后写入数据

11.2.2 读数据(read)

使用read(num)可以从文件中读取数据,num表示要从文件中读取的数据的长度(单位是字节),如果没有传入num,那么就表示读取文件中所有的数据,例:

f = open('test.txt', 'r')
content = f.read(5)
print(content)
print("-"*30)
content = f.read()
print(content)
f.close()

运行结果:

hello
------------------------------
 大家好, 我是AI浩

注意:

  • 如果open是打开一个文件,那么可以不用谢打开的模式,即只写 open('test.txt')
  • 如果使用读了多次,那么后面读取的数据是从上次读完后的位置开始的

11.2.3 读数据(readlines)

就像read没有参数时一样,readlines可以按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素

#coding=utf-8

f = open('test.txt', 'r')
content = f.readlines()
print(type(content))
i=1
for temp in content:
    print("%d:%s"%(i, temp))
    i+=1
f.close()

运行结果:

<class 'list'>
1:hello 大家好, 我是AI浩

11.2.4 读数据(readline)

#coding=utf-8

f = open('test.txt', 'r')
content = f.readline()
print("1:%s"%content)
content = f.readline()
print("2:%s"%content)

f.close()

运行结果:

1:hello 大家好, 我是AI浩

2:asfsifhiudh

11.3 文件的常用操作

11.3.1 获取当前读写的位置

在读写文件的过程中,如果想知道当前的位置,可以使用tell()来获取,例:

# 打开一个已经存在的文件
f = open("test.txt", "r")
str = f.read(3)
print("读取的数据是 : ", str)
# 查找当前位置
position = f.tell()
print("当前文件位置 : ", position)
str = f.read(3)
print("读取的数据是 : ", str)
# 查找当前位置
position = f.tell()
print("当前文件位置 : ", position)
f.close()

运行结果:

读取的数据是 :  hel
当前文件位置 :  3
读取的数据是 :  lo 
当前文件位置 :  6

11.3.2 定位到某个位置

如果在读写文件的过程中,需要从另外一个位置进行操作的话,可以使用seek()

seek(offset, from)有2个参数

  • offset:偏移量
  • from:方向
    • 0:表示文件开头
    • 1:表示当前位置
    • 2:表示文件末尾

例1:把位置设置为:从文件开头,偏移5个字节

# 打开一个已经存在的文件
f = open("test.txt", "r")
str = f.read(30)
print("读取的数据是 : ", str)

# 查找当前位置
position = f.tell()
print("当前文件位置 : ", position)
# 重新设置位置
f.seek(5, 0)
# 查找当前位置
position = f.tell()
print("当前文件位置 : ", position)
f.close()

例2:把位置设置为:离文件末尾,3字节处

# 打开一个已经存在的文件
f = open("test.txt", "rb")
print("读取的数据是 : ", str)
position = f.tell()
print("当前文件位置 : ", position)

# 重新设置位置
f.seek(-3, 2)

# 读取到的数据为:文件最后3个字节数据
str = f.read()
print("读取的数据是 : ", str)

f.close()

运行结果:

读取的数据是 :  <class 'str'>
当前文件位置 :  0
读取的数据是 :  b'ddf'

11.3.3 文件重命名

os模块中的rename()可以完成对文件的重命名操作

rename(需要修改的文件名, 新的文件名)

import os

os.rename("test.txt", "test-1.txt")

11.3.4 删除文件

os模块中的remove()可以完成对文件的删除操作

remove(待删除的文件名)

    import os
    os.remove("test.txt")

11.4 文件夹的相关操作

实际开发中,有时需要用程序的方式对文件夹进行一定的操作,比如创建、删除等

就像对文件操作需要os模块一样,如果要操作文件夹,同样需要os模块

11.4.1 创建文件夹

    import os

    os.mkdir("aa")

11.4.2 获取当前目录

    import os

    os.getcwd()

11.4.3 改变默认目录

    import os

    os.chdir("../")

11.4.4 获取目录列表

    import os

    os.listdir("./")

11.4.5 删除文件夹

    import os

    os.rmdir("张三")

11.4.6 检测文件夹是否存在

import os
if not os.path.exists(path):
      os.makedirs(path)

11.4.7 创建多级文件夹

import os
os.makedirs(opDir)

12 面向对象编程

面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。

面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。

而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。

12.1 类和对象

面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。

12.1.1 类

人以类聚 物以群分。
具有相似内部状态和运动规律的实体的集合(或统称为抽象)。 
具有相同属性和行为事物的统称

类是抽象的,在使用的时候通常会找到这个类的一个具体的存在,使用这个具体的存在。一个类可以找到多个对象

12.2.2 对象

某一个具体事物的存在 ,在现实世界中可以是看得见摸得着的。
可以是直接使用的

12.2.3 类和对象的关系

类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;

12.2.4 定义类

定义一个类,格式如下:

class 类名:
    方法列表

例:定义一个Car类

# 定义类
class Car:
    # 方法
    def getCarInfo(self):
        print('车轮子个数:%d, 颜色%s'%(self.wheelNum, self.color))

    def move(self):
        print("车正在移动...")

说明:

  • 定义类时有2种:新式类和经典类,上面的Car为经典类,如果是Car(object)则为新式类
  • 类名 的命名规则按照”大驼峰”

12.2.5 创建对象

通过上一节,定义了一个Car类;就好比有车一个张图纸,那么接下来就应该把图纸交给生成工人们去生成了

python中,可以根据已经定义的类去创建出一个个对象

创建对象的格式为:

对象名 = 类名()

创建对象demo:

# 定义类
class Car:
    # 移动
    def move(self):
        print('车在奔跑...')

    # 鸣笛
    def toot(self):
        print("车在鸣笛...嘟嘟..")


# 创建一个对象,并用变量BMW来保存它的引用
BMW = Car()
BMW.color = '黑色'
BMW.wheelNum = 4 #轮子数量
BMW.move()
BMW.toot()
print(BMW.color)
print(BMW.wheelNum)

运行结果:

车在奔跑...
车在鸣笛...嘟嘟..
黑色
4

12.2.6 __init__()方法

1)使用方式

def 类名:
    #初始化函数,用来完成一些默认的设定
    def __init__():
        pass

2)__init__()方法的调用

# 定义汽车类
class Car:

    def __init__(self):
        self.wheelNum = 4
        self.color = '蓝色'

    def move(self):
        print('车在跑,目标:夏威夷')

# 创建对象
BMW = Car()

print('车的颜色为:%s'%BMW.color)
print('车轮胎数量为:%d'%BMW.wheelNum)

运行结果:

车的颜色为:蓝色
车轮胎数量为:4

12.2.7 魔法方法

1)打印id()

如果把BMW使用print进行输出的话,会看到如下的信息,例:

# 定义汽车类
class Car:

    def __init__(self):
        self.wheelNum = 4
        self.color = '蓝色'

    def move(self):
        print('车在跑,目标:夏威夷')

# 创建对象
BMW = Car()

print(BMW)

运行结果:

<__main__.Car object at 0x0000014F596F8400>

即看到的是创建出来的BMW对象在内存中的地址

2) 定义__str__()方法

class Car:
    def __init__(self, newWheelNum, newColor):
        self.wheelNum = newWheelNum
        self.color = newColor
    def __str__(self):
        msg = "嘿。。。我的颜色是" + self.color + "我有" + str(self.wheelNum) + "个轮胎..."
        return msg

    def move(self):
        print('车在跑,目标:夏威夷')


BMW = Car(4, "白色")
print(BMW)

运行结果:

嘿。。。我的颜色是白色我有4个轮胎...

3)定义__del__()方法

创建对象后,python解释器默认调用__init__()方法;

当删除一个对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法

import time


class Animal(object):
    # 初始化方法
    # 创建完对象后会自动被调用
    def __init__(self, name):
        print('__init__方法被调用')
        self.__name = name

    # 析构方法
    # 当对象被删除时,会自动被调用
    def __del__(self):
        print("__del__方法被调用")
        print("%s对象马上被干掉了..." % self.__name)


# 创建对象
dog = Animal("哈皮狗")
# 删除对象
del dog
cat = Animal("波斯猫")
cat2 = cat
cat3 = cat
print("---马上 删除cat对象")
del cat
print("---马上 删除cat2对象")
del cat2
print("---马上 删除cat3对象")
del cat3
print("程序2秒钟后结束")
time.sleep(2)

运行结果:

__init__方法被调用
__del__方法被调用
哈皮狗对象马上被干掉了...
__init__方法被调用
---马上 删除cat对象
---马上 删除cat2对象
---马上 删除cat3对象
__del__方法被调用
波斯猫对象马上被干掉了...
程序2秒钟后结束
  • 当有1个变量保存了对象的引用时,此对象的引用计数就会加1
  • 当使用del删除变量指向的对象时,如果对象的引用计数不会1,比如3,那么此时只会让这个引用计数减1,即变为2,当再次调用del时,变为1,如果再调用1次del,此时会真的把对象进行删除

总结

  • 在python中方法名如果是__xxxx__()的,那么就有特殊的功能,因此叫做“魔法”方法
  • 当使用print输出对象的时候,只要自己定义了__str__(self)方法,那么就会打印从在这个方法中return的数据

常用的魔法方法列表

魔法方法 含义
基本的魔法方法
_new_(cls[, …]) 1. new 是在一个对象实例化的时候所调用的第一个方法 2. 它的第一个参数是这个类,其他的参数是用来直接传递给 init 方法 3. new 决定是否要使用该 init 方法,因为 new 可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果 new 没有返回实例对象,则 init 不会被调用 4. new 主要是用于继承一个不可变的类型比如一个 tuple 或者 string
_init_(self[, …]) 构造器,当一个实例被创建的时候调用的初始化方法
_del_(self) 析构器,当一个实例被销毁的时候调用的方法
_call_(self[, args…]) 允许一个类的实例像函数一样被调用:x(a, b) 调用 x.call(a, b)
_len_(self) 定义当被 len() 调用时的行为
_repr_(self) 定义当被 repr() 调用时的行为
_str_(self) 定义当被 str() 调用时的行为
_bytes_(self) 定义当被 bytes() 调用时的行为
_hash_(self) 定义当被 hash() 调用时的行为
_bool_(self) 定义当被 bool() 调用时的行为,应该返回 True 或 False
_format_(self, format_spec) 定义当被 format() 调用时的行为
有关属性
_getattr_(self, name) 定义当用户试图获取一个不存在的属性时的行为
_getattribute_(self, name) 定义当该类的属性被访问时的行为
_setattr_(self, name, value) 定义当一个属性被设置时的行为
_delattr_(self, name) 定义当一个属性被删除时的行为
_dir_(self) 定义当 dir() 被调用时的行为
_get_(self, instance, owner) 定义当描述符的值被取得时的行为
_set_(self, instance, value) 定义当描述符的值被改变时的行为
_delete_(self, instance) 定义当描述符的值被删除时的行为
比较操作符
_lt_(self, other) 定义小于号的行为:x < y 调用 x.lt(y)
_le_(self, other) 定义小于等于号的行为:x <= y 调用 x.le(y)
_eq_(self, other) 定义等于号的行为:x == y 调用 x.eq(y)
_ne_(self, other) 定义不等号的行为:x != y 调用 x.ne(y)
_gt_(self, other) 定义大于号的行为:x > y 调用 x.gt(y)
_ge_(self, other) 定义大于等于号的行为:x >= y 调用 x.ge(y)
算数运算符
_add_(self, other) 定义加法的行为:+
_sub_(self, other) 定义减法的行为:-
_mul_(self, other) 定义乘法的行为:*
_truediv_(self, other) 定义真除法的行为:/
_floordiv_(self, other) 定义整数除法的行为://
_mod_(self, other) 定义取模算法的行为:%
_divmod_(self, other) 定义当被 divmod() 调用时的行为
_pow_(self, other[, modulo]) 定义当被 power() 调用或 ** 运算时的行为
_lshift_(self, other) 定义按位左移位的行为:<<
_rshift_(self, other) 定义按位右移位的行为:>>
_and_(self, other) 定义按位与操作的行为:&
_xor_(self, other) 定义按位异或操作的行为:^
_or_(self, other) 定义按位或操作的行为:|
反运算
_radd_(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
_rsub_(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
_rmul_(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
_rtruediv_(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
_rfloordiv_(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
_rmod_(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
_rdivmod_(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
_rpow_(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
_rlshift_(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
_rrshift_(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
_rxor_(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
_ror_(self, other) (与上方相同,当左操作数不支持相应的操作时被调用)
增量赋值运算
_iadd_(self, other) 定义赋值加法的行为:+=
_isub_(self, other) 定义赋值减法的行为:-=
_imul_(self, other) 定义赋值乘法的行为:*=
_itruediv_(self, other) 定义赋值真除法的行为:/=
_ifloordiv_(self, other) 定义赋值整数除法的行为://=
_imod_(self, other) 定义赋值取模算法的行为:%=
_ipow_(self, other[, modulo]) 定义赋值幂运算的行为:**=
_ilshift_(self, other) 定义赋值按位左移位的行为:<<=
_irshift_(self, other) 定义赋值按位右移位的行为:>>=
_iand_(self, other) 定义赋值按位与操作的行为:&=
_ixor_(self, other) 定义赋值按位异或操作的行为:^=
_ior_(self, other) 定义赋值按位或操作的行为:|=
一元操作符
_neg_(self) 定义正号的行为:+x
_pos_(self) 定义负号的行为:-x
_abs_(self) 定义当被 abs() 调用时的行为
_invert_(self) 定义按位求反的行为:~x
类型转换
_complex_(self) 定义当被 complex() 调用时的行为(需要返回恰当的值)
_int_(self) 定义当被 int() 调用时的行为(需要返回恰当的值)
_float_(self) 定义当被 float() 调用时的行为(需要返回恰当的值)
_round_(self[, n]) 定义当被 round() 调用时的行为(需要返回恰当的值)
_index_(self) 1. 当对象是被应用在切片表达式中时,实现整形强制转换 2. 如果你定义了一个可能在切片时用到的定制的数值型,你应该定义 index 3. 如果 index 被定义,则 int 也需要被定义,且返回相同的值
上下文管理(with 语句)
_enter_(self) 1. 定义当使用 with 语句时的初始化行为 2. enter 的返回值被 with 语句的目标或者 as 后的名字绑定
_exit_(self, exc_type, exc_value, traceback) 1. 定义当一个代码块被执行或者终止后上下文管理器应该做什么 2. 一般被用来处理异常,清除工作或者做一些代码块执行完毕之后的日常工作
容器类型
_len_(self) 定义当被 len() 调用时的行为(返回容器中元素的个数)
_getitem_(self, key) 定义获取容器中指定元素的行为,相当于 self[key]
_setitem_(self, key, value) 定义设置容器中指定元素的行为,相当于 self[key] = value
_delitem_(self, key) 定义删除容器中指定元素的行为,相当于 del self[key]
_iter_(self) 定义当迭代容器中的元素的行为
_reversed_(self) 定义当被 reversed() 调用时的行为
_contains_(self, item) 定义当使用成员测试运算符(in 或 not in)时的行为

12.2.8 self

self可以理解为自己,类似C#中的this,例:

# 定义一个类
class Animal:

    # 方法
    def __init__(self, name):
        self.name = name

    def printName(self):
        print('名字为:%s' % self.name)


# 定义一个函数
def myPrint(animal):
    animal.printName()


dog1 = Animal('淘淘')
myPrint(dog1)
dog2 = Animal('贝贝')
myPrint(dog2)

运行结果:

名字为:淘淘
名字为:贝贝

总结

所谓的self,可以理解为自己

可以把self当做C++中类里面的this指针一样理解,就是对象自身的意思

某个对象调用其方法时,python解释器会把这个对象作为第一个参数传递给self,所以开发者只需要传递后面的参数即可

12.2 属性

属性分为类属性和实例属性。

类属性就是属于类所有,可以直接用类名.属性名直接调用,类的属性在内存中只有一份。实例属性就是在__init__()方法中初始化的属性;
实例属性属于类的对象所有,可以用对象名.属性名的形式进行调用,但是不能用类名.属性名进行调用 。因为实例属性只有在实例创建时,才会初始化创建。

12.2.1 类属性

类属性就是类对象所拥有的属性,它被所有类对象实例对象所共有,在内存中只存在一个副本,这个和C#中类的静态成员变量有点类似。对于公有的类属性,在类外可以通过类对象实例对象访问,例:

class People(object):
    name = 'Tom'  #公有的类属性
    __age = 12     #私有的类属性

p = People()

print(p.name)           #正确
print(People.name)      #正确
print(p.__age)            #错误,不能在类外通过实例对象访问私有的类属性
print(People.__age)        #错误,不能在类外通过类对象访问私有的类属性

类属性的访问:

import time


class Test(object):
    name = 'scolia'


a = Test()
print(Test.name)  # 通过类进行访问
print(a.name)  # 通过实例进行访问

运行结果:

scolia
scolia

可以访问的,但是,试图修改这个属性的话:

class Test(object):
    name = 'scolia'


a = Test()
Test.name = 'scolia good'  # 通过类进行修改
print(Test.name)
print(a.name)

运行结果:

scolia good
scolia good

我们发现两者都修改成功了。再尝试通过实例来修改属性的话:

class Test(object):
    name = 'scolia'

a = Test()
a.name = 'scolia good'  # 通过实例进行修改
print(Test.name)
print(a.name)

运行结果:

scolia
scolia good

我们发现类的属性没有修改,而实例的属性则修改成功了。这究竟是为什么?

其实这里的情况非常类似于局部作用域和全局作用域。

我在函数内访问变量时,会先在函数内部查询有没有这个变量,如果没有,就到外层中找。这里的情况是我在实例中访问一个属性,但是我实例中没有,我就试图去创建我的类中寻找有没有这个属性。找到了,就有,没找到,就抛出异常。而当我试图用实例去修改一个在类中不可变的属性的时候,我实际上并没有修改,而是在我的实例中创建了这个属性。而当我再次访问这个属性的时候,我实例中有,就不用去类中寻找了。如果用一张图来表示的话:

下载

12.2.2 实例属性(对象属性)

实例属性是在_init_(self)方法定义的属性,属于对象的本身,只能通过对象.属性来访问,不能用过类.属性来访问。例:

class People(object):
    address = '山东' #类属性
    def __init__(self):
        self.name = 'xiaowang' #实例属性
        self.age = 20 #实例属性

p = People()
p.age =12 #实例属性
print(p.address) #正确
print(p.name)    #正确
print(p.age)     #正确

print(People.address) #正确
print(People.name)    #错误
print(People.age)     #错误

12.2.3 私有属性

如果有一个对象,当需要对其进行修改属性时,有2种方法

  • 对象名.属性名 = 数据 —->直接修改
  • 对象名.方法名() —->间接修改

为了更好的保存属性安全,即不能随意修改,一般的处理方式为

  • 将属性定义为私有属性
  • 添加一个可以调用的方法,供调用
class People(object):

    def __init__(self, name):
        self.__name = name

    def getName(self):
        return self.__name

    def setName(self, newName):
        if len(newName) >= 5:
            self.__name = newName
        else:
            print("error:名字长度需要大于或者等于5")

xiaoming = People("AI浩")
print(xiaoming.__name)
Traceback (most recent call last):
  File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 16, in <module>
    print(xiaoming.__name)
AttributeError: 'People' object has no attribute '__name'

直接调用私有属性会报错,我需要定义个方法,调用方法

class People(object):

    def __init__(self, name):
        self.__name = name

    def getName(self):
        return self.__name

    def setName(self, newName):
        if len(newName) >= 5:
            self.__name = newName
        else:
            print("error:名字长度需要大于或者等于5")


xiaoming = People("张三")

xiaoming.setName("wanger")
print(xiaoming.getName())

xiaoming.setName("lisi")
print(xiaoming.getName())

运行结果:

wanger
error:名字长度需要大于或者等于5
wanger

总结

  • 如果需要在类外修改类属性,必须通过类对象去引用然后进行修改。如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性
  • Python中没有像C++中public和private这些关键字来区别公有属性和私有属性
  • 它是以属性命名方式来区分,如果在属性名前面加了2个下划线’__’,则表明该属性是私有属性,否则为公有属性(方法也是一样,方法名前面加了2个下划线的话表示该方法是私有的,否则为公有的)。

12.3 继承

在程序中,继承描述的是事物之间的所属关系,例如猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物;同理,波斯猫和巴厘猫都继承自猫,而沙皮狗和斑点狗都继承足够,如下如所示:

继承

# 定义一个父类,如下:
class Cat(object):

    def __init__(self, name, color="白色"):
        self.name = name
        self.color = color

    def run(self):
        print("%s--在跑" % self.name)


# 定义一个子类,继承Cat类如下:
class Bosi(Cat):

    def setNewName(self, newName):
        self.name = newName

    def eat(self):
        print("%s--在吃" % self.name)


bs = Bosi("印度猫")
print('bs的名字为:%s' % bs.name)
print('bs的颜色为:%s' % bs.color)
bs.eat()
bs.setNewName('波斯')
bs.run()

运行结果:

bs的名字为:印度猫
bs的颜色为:白色
印度猫--在吃
波斯--在跑

结论:

  • 虽然子类没有定义__init__方法,但是父类有,所以在子类继承父类的时候这个方法就被继承了,所以只要创建Bosi的对象,就默认执行了那个继承过来的__init__方法

  • 子类在继承的时候,在定义类时,小括号()中为父类的名字

  • 父类的属性、方法,会被继承给子类

12.3.1 私有属性和方法的继承

class Animal(object):

    def __init__(self, name='动物', color='白色'):
        self.__name = name
        self.color = color

    def __test(self):
        print(self.__name)
        print(self.color)

    def test(self):
        print(self.__name)
        print(self.color)


class Dog(Animal):
    def dogTest1(self):
        # print(self.__name) #不能访问到父类的私有属性
        print(self.color)

    def dogTest2(self):
        # self.__test() #不能访问父类中的私有方法
        self.test()


A = Animal()
# print(A.__name) #程序出现异常,不能访问私有属性
print(A.color)
# A.__test() #程序出现异常,不能访问私有方法
A.test()

print("------分割线-----")

D = Dog(name="小花狗", color="黄色")
D.dogTest1()
D.dogTest2()

运行结果:


白色
动物
白色
------分割线-----
黄色
小花狗
黄色

结论:

私有的属性,不能通过对象直接访问,但是可以通过方法访问
私有的方法,不能通过对象直接访问
私有的属性、方法,不会被子类继承,也不能被访问
一般情况下,私有的属性、方法都是不对外公布的,往往用来做内部的事情,起到安全的作用

12.3.2 多继承

Python中多继承的格式如下:

# 定义一个父类
class A:
    def printA(self):
        print('----A----')

# 定义一个父类
class B:
    def printB(self):
        print('----B----')

# 定义一个子类,继承自A、B
class C(A,B):
    def printC(self):
        print('----C----')

obj_C = C()
obj_C.printA()
obj_C.printB()

运行结果:

----A----
----B----

结论:

python中是可以多继承的
父类中的方法、属性,子类会继承

如果父类A和父类B中,有一个同名的方法,那么通过子类去调用的时候,调用哪个?

# coding=utf-8
class base(object):
    def test(self):
        print('----base test----')


class A(base):
    def test(self):
        print('----A test----')


# 定义一个父类
class B(base):
    def test(self):
        print('----B test----')


# 定义一个子类,继承自A、B
class C(A, B):
    pass


obj_C = C()
obj_C.test()

print("搜索顺序:",C.__mro__)  # 可以查看C类的对象搜索方法时的先后顺序

运行结果:

----A test----
搜索顺序: (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.base'>, <class 'object'>)

12.3.3 重写父类方法

所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法

class Cat(object):
    def sayHello(self):
        print("halou-----1")


class Bosi(Cat):

    def sayHello(self):
        print("halou-----2")


bosi = Bosi()
bosi.sayHello()

运行结果:

halou-----2

12.3.4 调用父类的方法

调用父类的__init__方法

#coding=utf-8
class Cat(object):
    def __init__(self,name):
        self.name = name
        self.color = 'yellow'


class Bosi(Cat):

    def __init__(self,name):
        # 调用父类的__init__方法1(python2)
        #Cat.__init__(self,name)
        # 调用父类的__init__方法2
        #super(Bosi,self).__init__(name)
        # 调用父类的__init__方法3
        super().__init__(name)

    def getName(self):
        return self.name

bosi = Bosi('xiaohua')
print(bosi.name)
print(bosi.color)

运行结果:

xiaohua
yellow

调用父类的普通方法

# 父类
class Dog:
  def bark(self):
    print("汪汪叫")
# 子类 继承
class XiaoTianQuan(Dog):
  def fly(self):
    print("我会飞")
  # 可以重写父类中的同名方法
  def bark(self):
    # super().父类方法名 调用父类中的方法 (第一种方式)(推荐)
    super().bark()
    # 父类名.方法(self) 调用父类中的方法 (第二种方式,python2.x)(不推荐,父类名修改后,此处也得改)
    Dog.bark(self)
    # 注意:如果使用子类名调用方法,可能会出现递归调用 -- 死循环!
    # XiaoTianQuan.bark(self) # 会产生死循环
    # 针对子类特有的需求,进行扩展
    print("神一样的叫唤...")
xtq = XiaoTianQuan()
xtq.bark()

运行结果:

汪汪叫
汪汪叫
神一样的叫唤...

12.4 多态

多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型”。

所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态,例:Python “鸭子类型”

class F1(object):
    def show(self):
        print ('F1.show')

class S1(F1):

    def show(self):
        print ('S1.show')

class S2(F1):

    def show(self):
        print ('S2.show')

def Func(obj):
    print (obj.show())

s1_obj = S1()
Func(s1_obj)
s2_obj = S2()
Func(s2_obj)

运行结果:

S1.show
None
S2.show
None

12.5 静态方法和类方法

12.5.1 类方法

是类对象所拥有的方法,需要用修饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以’cls’作为第一个参数的名字,就最好用’cls’了),能够通过实例对象和类对象去访问。

class People(object):
    country = 'china'

    # 类方法,用classmethod来进行修饰
    @classmethod
    def getCountry(cls):
        return cls.country

p = People()
print(p.getCountry())  # 可以用过实例对象引用
print(People.getCountry())  # 可以通过类对象引用

类方法还有一个用途就是可以对类属性进行修改:

class People(object):
    country = 'china'

    # 类方法,用classmethod来进行修饰
    @classmethod
    def getCountry(cls):
        return cls.country

    @classmethod
    def setCountry(cls, country):
        cls.country = country


p = People()
print(p.getCountry())  # 可以用过实例对象引用
print(People.getCountry())  # 可以通过类对象引用
p.setCountry('japan')
print(p.getCountry())
print(People.getCountry())

运行结果:

china
china
japan
japan

结果显示在用类方法对类属性修改之后,通过类对象和实例对象访问都发生了改变

12.5.2 静态方法

需要通过修饰器@staticmethod来进行修饰,静态方法不需要多定义参数

class People(object):
    country = 'china'

    @staticmethod
    #静态方法
    def getCountry():
        return People.country


print People.getCountry()

总结

从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;而实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用

13 异常

当Python检测到一个错误时,解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的”异常”。

13.1 捕获异常

13.1.1 捕获异常 try…except…

看如下示例:

try:
    print('-----test--1---')
    open('123.txt','r')
    print('-----test--2---')
except IOError:
    pass

运行结果:

-----test--1---

说明:

  • 此程序看不到任何错误,因为用except 捕获到了IOError异常,并添加了处理的方法
  • pass 表示实现了相应的实现,但什么也不做;如果把pass改为print语句,那么就会输出其他信息

小总结:

img

  • 把可能出现问题的代码,放在try中
  • 把处理异常的代码,放在except中

13.1.2 except捕获多个异常

看如下示例:

try:
    print (num)
except IOError:
    print('产生错误了')

运行结果如下:

Traceback (most recent call last):
  File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 2, in <module>
    print(num)
NameError: name 'num' is not defined

想一想:

上例程序,已经使用except来捕获异常了,为什么还会看到错误的信息提示?

答:

except捕获的错误类型是IOError,而此时程序产生的异常为 NameError ,所以except没有生效

修改后的代码为:

try:
    print (num)
except NameError:
    print('产生错误了')

运行结果如下:

产生错误了

实际开发中,捕获多个异常的方式,如下:

#coding=utf-8
try:
    print('-----test--1---')
    open('123.txt','r') # 如果123.txt文件不存在,那么会产生 IOError 异常
    print('-----test--2---')
    print(num)# 如果num变量没有定义,那么会产生 NameError 异常

except (IOError,NameError):
    #如果想通过一次except捕获到多个异常可以用一个元组的方式
    # errorMsg里会保存捕获到的错误信息
    print(errorMsg)

运行结果:

-----test--1---
Traceback (most recent call last):
  File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 4, in <module>
    open('123.txt','r') # 如果123.txt文件不存在,那么会产生 IOError 异常
FileNotFoundError: [Errno 2] No such file or directory: '123.txt'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 12, in <module>
    print(errorMsg)
NameError: name 'errorMsg' is not defined

注意:

  • 当捕获多个异常时,可以把要捕获的异常的名字,放到except 后,并使用元组的方式仅进行存储

13.1.3 获取异常的信息描述

img

img

13.1.4捕获所有异常

img

img

13.1.5 else

咱们应该对else并不陌生,在if中,它的作用是当条件不满足时执行的实行;同样在try…except…中也是如此,即如果没有捕获到异常,那么就执行else中的事情

try:
    num = 100
    print (num)
except NameError as errorMsg:
    print('产生错误了:%s'%errorMsg)
else:
    print('没有捕获到异常,真高兴')

运行结果如下:

100
没有捕获到异常,真高兴

13.1.6 try…finally…

try…finally…语句用来表达这样的情况:

在程序中,如果一个段代码必须要执行,即无论异常是否产生都要执行,那么此时就需要使用finally。 比如文件关闭,释放锁,把数据库连接返还给连接池等

demo:

import time
try:
    f = open('test.txt')
    try:
        while True:
            content = f.readline()
            if len(content) == 0:
                break
            time.sleep(2)
            print(content)
    except:
        #如果在读取文件的过程中,产生了异常,那么就会捕获到
        #比如 按下了 ctrl+c
        pass
    finally:
        f.close()
        print('关闭文件')
except:
    print("没有这个文件")

说明:

test.txt文件中每一行数据打印,但是我有意在每打印一行之前用time.sleep方法暂停2秒钟。这样做的原因是让程序运行得慢一些。在程序运行的时候,按Ctrl+c中断(取消)程序。

我们可以观察到KeyboardInterrupt异常被触发,程序退出。但是在程序退出之前,finally从句仍然被执行,把文件关闭。

13.2 异常的传递

13.2.1. try嵌套中

import time
try:
    f = open('test.txt')
    try:
        while True:
            content = f.readline()
            if len(content) == 0:
                break
            time.sleep(2)
            print(content)
    finally:
        f.close()
        print('关闭文件')
except:
    print("没有这个文件")

运行结果:

没有这个文件

13.2.2 函数嵌套调用中

def test1():
    print("----test1-1----")
    print(num)
    print("----test1-2----")


def test2():
    print("----test2-1----")
    test1()
    print("----test2-2----")


def test3():
    try:
        print("----test3-1----")
        test1()
        print("----test3-2----")
    except Exception as result:
        print("捕获到了异常,信息是:%s" % result)

    print("----test3-2----")


test3()
print("------华丽的分割线-----")
test2()

运行结果:

----test3-1----
----test1-1----
捕获到了异常,信息是:name 'num' is not defined
----test3-2----
------华丽的分割线-----
----test2-1----
----test1-1----
Traceback (most recent call last):
  File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 26, in <module>
    test2()
  File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 9, in test2
    test1()
  File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 3, in test1
    print(num)
NameError: name 'num' is not defined

总结:

  • 如果try嵌套,那么如果里面的try没有捕获到这个异常,那么外面的try会接收到这个异常,然后进行处理,如果外边的try依然没有捕获到,那么再进行传递。。。
  • 如果一个异常是在一个函数中产生的,例如函数A—->函数B—->函数C,而异常是在函数C中产生的,那么如果函数C中没有对这个异常进行处理,那么这个异常会传递到函数B中,如果函数B有异常处理那么就会按照函数B的处理方式进行执行;如果函数B也没有异常处理,那么这个异常会继续传递,以此类推。。。如果所有的函数都没有处理,那么此时就会进行异常的默认处理,即通常见到的那样
  • 注意观察上图中,当调用test3函数时,在test1函数内部产生了异常,此异常被传递到test3函数中完成了异常处理,而当异常处理完后,并没有返回到函数test1中进行执行,而是在函数test3中继续执行

13.3 抛出自定义的异常

你可以用raise语句来引发一个异常。异常/错误对象必须有一个名字,且它们应是Error或Exception类的子类

下面是一个引发异常的例子:

class ShortInputException(Exception):
    '''自定义的异常类'''
    def __init__(self, length, atleast):
        #super().__init__()
        self.length = length
        self.atleast = atleast

def main():
    try:
        s = input('请输入 --> ')
        if len(s) < 3:
            # raise引发一个你定义的异常
            raise ShortInputException(len(s), 3)
    except ShortInputException as result:#x这个变量被绑定到了错误的实例
        print('ShortInputException: 输入的长度是 %d,长度至少应是 %d'% (result.length, result.atleast))
    else:
        print('没有异常发生.')

main()

运行结果如下:

请输入 --> 11
ShortInputException: 输入的长度是 2,长度至少应是 3

注意

  • 以上程序中,关于代码#super().init()的说明

    这一行代码,可以调用也可以不调用,建议调用,因为__init__方法往往是用来对创建完的对象进行初始化工作,如果在子类中重写了父类的__init__方法,即意味着父类中的很多初始化工作没有做,这样就不保证程序的稳定了,所以在以后的开发中,如果重写了父类的__init__方法,最好是先调用父类的这个方法,然后再添加自己的功能

13.4 异常处理中抛出异常

class Test(object):
    def __init__(self, switch):
        self.switch = switch #开关
    def calc(self, a, b):
        try:
            return a/b
        except Exception as result:
            if self.switch:
                print("捕获开启,已经捕获到了异常,信息如下:")
                print(result)
            else:
                #重新抛出这个异常,此时就不会被这个异常处理给捕获到,从而触发默认的异常处理
                raise


a = Test(True)
a.calc(11,0)

print("----------------------华丽的分割线----------------")

a.switch = False
a.calc(11,0)

运行结果:

捕获开启,已经捕获到了异常,信息如下:
division by zero
----------------------华丽的分割线----------------
Traceback (most recent call last):
  File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 21, in <module>
    a.calc(11,0)
  File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 6, in calc
    return a/b
ZeroDivisionError: division by zero

14 模块

14.1 模块介绍

14.1.1 Python中的模块

有过C语言编程经验的朋友都知道在C语言中如果要引用sqrt函数,必须用语句#include <math.h>引入math.h这个头文件,否则是无法正常进行调用的。

那么在Python中,如果要引用一些其他的函数,该怎么处理呢?

在Python中有一个概念叫做模块(module),这个和C语言中的头文件以及Java中的包很类似,比如在Python中要调用sqrt函数,必须用import关键字引入math这个模块,下面就来了解一下Python中的模块。

说的通俗点:模块就好比是工具包,要想使用这个工具包中的工具(就好比函数),就需要导入这个模块

14.1.2 import

在Python中用关键字import来引入某个模块,比如要引用模块math,就可以在文件最开始的地方用import math来引入。

形如:

    import module1,mudule2...

当解释器遇到import语句,如果模块在当前的搜索路径就会被导入。

在调用math模块中的函数时,必须这样引用:

  模块名.函数名
  • 想一想:

    为什么必须加上模块名调用呢?

  • 答:

    因为可能存在这样一种情况:在多个模块中含有相同名称的函数,此时如果只是通过函数名来调用,解释器无法知道到底要调用哪个函数。所以如果像上述这样引入模块的时候,调用函数必须加上模块名

    import math

    #这样会报错
    print sqrt(2)

    #这样才能正确输出结果
    print math.sqrt(2)

有时候我们只需要用到模块中的某个函数,只需要引入该函数即可,此时可以用下面方法实现:

    from 模块名 import 函数名1,函数名2....

不仅可以引入函数,还可以引入一些全局变量、类等

  • 注意:

    • 通过这种方式引入的时候,调用函数时只能给出函数名,不能给出模块名,但是当两个模块中含有相同名称函数的时候,后面一次引入会覆盖前一次引入。也就是说假如模块A中有函数function( ),在模块B中也有函数function( ),如果引入A中的function在先、B中的function在后,那么当调用function函数的时候,是去执行模块B中的function函数。
    • 如果想一次性引入math中所有的东西,还可以通过from math import *来实现

14.1.3 from…import

Python的from语句让你从模块中导入一个指定的部分到当前命名空间中

语法如下:

    from modname import name1[, name2[, ... nameN]]

例如,要导入模块fib的fibonacci函数,使用如下语句:

    from fib import fibonacci

注意

  • 不会把整个fib模块导入到当前的命名空间中,它只会将fib里的fibonacci单个引入

14.1.4 from … import *

把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:

    from modname import *

注意

  • 这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。

14.1.5 as

    In [1]: import time as tt
In [2]: time.sleep(1)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-2-07a34f5b1e42> in <module>()
----> 1 time.sleep(1)
NameError: name 'time' is not defined
In [3]: 
In [3]: 
In [3]: tt.sleep(1)
In [4]: 
In [4]: 
In [4]: from time import sleep as sp
In [5]: sleep(1)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-5-82e5c2913b44> in <module>()
----> 1 sleep(1)
NameError: name 'sleep' is not defined
In [6]: 
In [6]: 
In [6]: sp(1)
In [7]:

14.1.6 定位模块

当你导入一个模块,Python解析器对模块位置的搜索顺序是:

  1. 当前目录
  2. 如果不在当前目录,Python则搜索在shell变量PYTHONPATH下的每个目录。
  3. 如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/python/
  4. 模块搜索路径存储在system模块的sys.path变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。

14.2 制作模块

14.2.1 定义自己的模块

在Python中,每个Python文件都可以作为一个模块,模块的名字就是文件的名字。

比如有这样一个文件test.py,在test.py中定义了函数add

test.py

    def add(a,b):
return a+b

14.2.2 调用自己定义的模块

那么在其他文件中就可以先import test,然后通过test.add(a,b)来调用了,当然也可以通过from test import add来引入

main.py

    import test
result = test.add(11,22)
print(result)

14.2.3 测试模块

在实际开中,当一个开发人员编写完一个模块后,为了让模块能够在项目中达到想要的效果,这个开发人员会自行在py文件中添加一些测试信息,例如:

test.py
def add(a,b):
return a+b
# 用来进行测试
ret = add(12,22)
print('int test.py file,,,,12+22=%d'%ret)

如果此时,在其他py文件中引入了此文件的话,想想看,测试的那段代码是否也会执行呢!

main.py
import test
result = test.add(11,22)
print(result)

运行现象:

int test.py file,,,,12+22=34
33

至此,可发现test.py中的测试代码,应该是单独执行test.py文件时才应该执行的,不应该是其他的文件中引用而执行

为了解决这个问题,python在执行一个文件时有个变量__name__

直接运行此文件

def add(a, b):
return a + b
# # 用来进行测试
# ret = add(12,22)
# print('int test.py file,,,,12+22=%d'%ret)
print("in test.py file,__name__ is %s "%__name__)

运行结果:

in test.py file,__name__ is __main__ 

在其他文件中import此文件

in test.py file,__name__ is test 
33

总结:

  • 可以根据__name__变量的结果能够判断出,是直接执行的python脚本还是被引入执行的,从而能够有选择性的执行测试代码

    执行test.py:

def add(a, b):
return a + b
if __name__ == "__main__":
# 用来进行测试
ret = add(12, 22)
print('int test.py file,,,,12+22=%d' % ret)
print("in test.py file,__name__ is %s " % __name__)

运行结果:

D:\ProgramData\Anaconda3\python.exe C:/Users/WH/Desktop/Python基础/test.py
int test.py file,,,,12+22=34
in test.py file,__name__ is __main__ 

执行main.py :

import test
result = test.add(11, 22)
print(result)

运行结果:

33

14.3 模块中的__all__

14.3.1 没有__all__

test.py

class Test(object):
def test(self):
print("Test类中的test函数")
def test1():
print("test1函数")
def test2():
print("test2函数")

main.py调用

from test import *
a=Test()
print(a.test())
print(test1())
print(test2())

运行结果:

Test类中的test函数
None
test1函数
None
test2函数
None

14.3.2. 模块中有__all__

__all__=["Test","test1"]
class Test(object):
def test(self):
print("Test类中的test函数")
def test1():
print("test1函数")
def test2():
print("test2函数")

执行main.py

from test import *
a=Test()
print(a.test())
print(test1())
print(test2())

运行结果:

Traceback (most recent call last):
File "C:/Users/WH/Desktop/Python基础/第一个Python程序.py", line 6, in <module>
print(test2())
NameError: name 'test2' is not defined
Test类中的test函数
None
test1函数
None

总结

如果一个文件中有__all__变量,那么也就意味着这不在变量中的元素,不会被from xxx import *时导入

14.4 python中的包

14.4.1. 引入包

有2个模块功能有些联系

img

所以将其放到同一个文件夹下

img

使用import 文件.模块 的方式导入

img

使用from 文件夹 import 模块 的方式导入

img

在msg文件夹下创建__init__.py文件

img

__init__.py文件中写入

img

重新使用from 文件夹 import 模块 的方式导入

img

总结:

  • 包将有联系的模块组织在一起,即放到同一个文件夹下,并且在这个文件夹创建一个名字为__init__.py 文件,那么这个文件夹就称之为
  • 有效避免模块名称冲突问题,让应用组织结构更加清晰

14.4.2 __init__.py文件作用

__init__.py 控制着包的导入行为

__init__.py为空,仅仅是把这个包导入,不会导入包中的模块

__all____init__.py文件中,定义一个__all__变量,它控制着 from 包名 import *时导入的模块

14.5 模块发布

1.mymodule目录结构体如下:

.
├── setup.py
├── suba
│   ├── aa.py
│   ├── bb.py
│   └── __init__.py
└── subb
├── cc.py
├── dd.py
└── __init__.py

2.编辑setup.py文件

py_modules需指明所需包含的py文件

from distutils.core import setup
setup(name="AIhao", version="1.0", description="AIhao's module", author="AIhao", py_modules=['suba.aa', 'suba.bb', 'subb.cc', 'subb.dd'])

3.构建模块

python setup.py build

构建后目录结构

.
├── build
│   └── lib.linux-i686-2.7
│       ├── suba
│       │   ├── aa.py
│       │   ├── bb.py
│       │   └── __init__.py
│       └── subb
│           ├── cc.py
│           ├── dd.py
│           └── __init__.py
├── setup.py
├── suba
│   ├── aa.py
│   ├── bb.py
│   └── __init__.py
└── subb
├── cc.py
├── dd.py
└── __init__.py

4.生成发布压缩包

python setup.py sdist

打包后,生成最终发布压缩包AIhao-1.0.tar.gz , 目录结构

.
├── build
│   └── lib.linux-i686-2.7
│       ├── suba
│       │   ├── aa.py
│       │   ├── bb.py
│       │   └── __init__.py
│       └── subb
│           ├── cc.py
│           ├── dd.py
│           └── __init__.py
├── dist
│   └── AIhao-1.0.tar.gz
├── MANIFEST
├── setup.py
├── suba
│   ├── aa.py
│   ├── bb.py
│   └── __init__.py
└── subb
├── cc.py
├── dd.py
└── __init__.py

14.6 模块安装、使用

14.6.1 安装的方式

  1. 找到模块的压缩包
  2. 解压
  3. 进入文件夹
  4. 执行命令python setup.py install

注意:

  • 如果在install的时候,执行目录安装,可以使用python setup.py install --prefix=安装路径

14.6.2 模块的引入

在程序中,使用from import 即可完成对安装的模块使用

from 模块名 import 模块名或者*

PDF版本:https://download.csdn.net/download/hhhhhhhhhhwwwwwwwwww/21839010?spm=1001.2014.3001.5501

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/152879.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • CSS样式表定义

    CSS样式表定义一个JSP页面是通过在HTML标签的基础上嵌入JSP动作和指令、CSS、Java变量和方法(Java代码段)、其他脚本元素(如JavaScript)等组成的。

  • sqlmap怎么安装_sqlmapchik

    sqlmap怎么安装_sqlmapchik一前提需要安装python2,可以看“python2下载与安装”二下载官网下载三安装将下载的sqlmap.zip解压到文件夹sqlmap中,并拷贝到Python安装路径下四配

  • PL/SQL Developer连不上数据库解决方法

    PL/SQL Developer连不上数据库解决方法

  • goland 2021.7激活码【2021免费激活】

    (goland 2021.7激活码)本文适用于JetBrains家族所有ide,包括IntelliJidea,phpstorm,webstorm,pycharm,datagrip等。https://javaforall.cn/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~M…

  • pub格式的文件打开报错:Publisher无法打开文件 解决办法[通俗易懂]

    pub格式的文件打开报错:Publisher无法打开文件 解决办法[通俗易懂]最近搞Git上传文件到Github仓库遇到了点问题:id_rsa.pub这个文件打不开,但是我需要复制内容生成ssh密钥文件格式类型为:MicrosoftPublisherDocument(.pub)解决方法:1、在当前文件目录输入cmd【直接定位到当前目录下】2、dos下查看文件内容的命令:type文件名3、复制内容就可以去粘贴使用啦,希望可以帮到你们!!!…

    2022年10月14日
  • Idea中的war和war explore有什么区别

    Idea中的war和war explore有什么区别我们发布项目时选择war还是war explore:war模式:将web工程以war包的形式上传到服务器 war exploed模式:将web工程以当前文件夹的位置关系上传到服务器war模式:先打成war包,然后再发布到Tomcat的ROOT下,所以选择这个后,你再看下你的tomcat的root文件夹下,是刚打成的项目war exploded模式:只编译,生成target;然后把当…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号