第一部分:基础

一、数据类型

python拥有6种数据类型,分别为:

a、数字 i=1 b、字符串 a="1" c、列表 a=[1] d、元组 a=(1) e、集合 a={1} f、字典 a={1:"one"}

所有数据类型都用=进行赋值操作,赋值时不指定类型(动态类型)

1、数字

数字类型最简单,把一个数字类型赋值给一个变量,直接在=后面输入数字即可 如 a=1

数字分为整数和浮点数及其他,比如:

print(type(1),type(1.0))

输出结果为

<class 'int'> <class 'float'>

2、字符串

和数字类型的区别在于:字符串需要用""或者''包含起来

a="我是字母a"a='我是字母b' 都可以,看个人喜好,'5','6',“7”都是字符串,而5,6,7是数字

上述情况下,字符串可以转换为数字,比如通过一个int()函数

print(int("9"))

输出结果为:

9

为什么要用“”或者‘’把字符串包含起来呢?因为不包含‘’“的用作变量名。。。比如上面的a

3、列表

列表是一堆元素的集合,用[]包含起来,用,分开各个元素

a=[1,2,3,4,5]a=["1",2,"3","four",[5]] 列表里可包含任何数据类型,当然也可以包含一个列表

列表的索引用[],第一个元素为0,以此类推

a=[1,2,"3",[0,2],{1,3},("one"),{9:"one"}]
print(a[0],a[3],a[3][1],a[5],a[6][9])

输出结果为

1 [0, 2] 2 one one

4、元组

和列表很相似,元组是用()包含起来,统一用,分开各个元素

a=(1,2,3,4,5)a=(1,"two",(3),[4],{5},{6:"six"})

元组的索引和列表一样,也是用[],第一个元素为0

a=(1,2,"3",[0,2],{1,3},("one"),{9:"one"})
print(a[0],a[3],a[3][1],a[5],a[6][9])

输出结果为

1 [0, 2] 2 one one

那元组和列表的区别在哪里呢? 在于元组不能修改元素内容

5、集合

集合和列表也有点相似,用{}包含起来,用,分开各个元素 ,a={3,2,1}

集合有几个特殊的地方:

1、集合自动排序且去重

a={3,2,1,3,"five"}
print(a)

输出结果为:

{1, 2, 3, 'five'}

2、集合不支持索引

a={3,2,1,3,"five"}
print(a[0])

输出结果为:

'set' object does not support indexing

6、字典

字典的赋值方式和集合很像,都是用{},但字典字典,顾名思义:有“键”,有“值“,在字典里的排序是这样的:

a={1:"one",2:"two","three":3,4:[0,2]} ,用:区分键和值,用,分开每个元素,对于元素的内容不限制

字典的索引和列表、元组的区别在于,索引项里需要填写键值

a={1:"one",2:"two","three":3,4:[0,2]}
print(a[4][0],a["three"])

输出结果

0 3

7、比较

数据类型创建方式可否索引可否修改空类型方法其他
数字数字-不可为空 
字符串""或''-""或'' 
列表[ ]是,从0开始[] 
元组( )是,从0开始() 
集合{ }函数方式自动排序去重
字典{ }是,按"键"值{} 

 

二、关键字

 

1、关键字查看

python中查看所有关键字的方法:

import keyword
for i in keyword.kwlist:
    print(i,"",end="")

输出结果为

False None True and as assert break class continue def del elif else except finally for from global if import in is lambda nonlocal not or pass raise return try while with yield

在上面的代码中,import 、for、in、print就是关键字,合计33个,我们按功能进行区分讲解

2、数据类型关键字

第一节中讲了python的6种数据类型,除了这6种外,python还有一些特殊的数据类型

a、布尔类型

布尔类型分别为 TrueFalse,多用于判断,比如在if 语句中,就是判断后面的语法是不是正确

if 2>1:
    print("正确")
if True:
    print("正确")

输出结果为

正确 正确

b、空类型

None 是一个空类型,不是0,不是False,比如一个函数没有返回值,但强行赋值给它时

def test():
    pass
a=test()
print(type(a))

输出结果为:

<class 'NoneType'>

这里涉及到函数及返回值,后面详细介绍

3、运算符关键字

① 普通的运算符,即我们常用的 + - * / 分别是加、减、乘、除,//表示求除数,%表示求余数,**表示求几次方。

print(3+2,3-2,3*2,3/2,3//2,3%2,3**2)

输出结果为

5 1 6 1.5 1 1 9

注意,两个int相除会生成一个float,即便从数学上来说结果没有小数点

print(1/1)

输出结果为

1.0

② 除了上面普通的运算符外,还有==!=><<=>=is,这些称作比较运算符,比较运算符可以生成一个True或者False

print(1==1,1!=1,1<2,1>2,1<=2,1>=2)

输出结果为:

True False True False True False

注意,===容易混淆,使用时注意

is是个比较特殊的比较运算符,和==不同:==是确定两个数据是否相等,is是确定两个数据是否为同一对象

a=b=[1,2]
c=[1,2]
print(a==c,a is c,a is b)
print(id(a),id(b),id(c))

输出结果为:

True False True
18269064 18269064 18269000

 

可知,在python中,赋值时有时会仅仅添加一个映射,有时是新增一个空间存储数据,后面章节详细描述

③ 布尔运算符,布尔运算是对TrueFalse的运算

print(True and False,True or False,not True)

输出结果为

False True False

and 是左右都为True才为Trueor 是有一个为True就是Truenot是取反

注意,两个False and运算还是False,从生成True的角度去理解

4、控制关键字

if 是一个很重要的关键字,用于判断某个结果是True还是False,进而确定下一步动作,if通常与else联用

a=10
if a>9:
    print("a大于9")
else:
    if a>8:
        print("a大于8")
    else:
        if a>7:
            print("a大于7")

输出结果为:

a大于8

如上所示,在if后面需要有一个:(冒号),其内容要缩进表示(相比C语言的大括号)

上面例子可以知道,如果条件很多,需要多个缩进,不利于书写,所以有elif关键字

a=10
if a>9:
    print("a大于9")
elif a>8:
    print("a大于8")
elif a>7:
    print("a大于7")

输出结果和上面相同

如果一个if满足条件,则else或者elif不再执行,退出本次判断

5、循环关键字

a、for

python里的for和C语言的不一样,C语言通常是

for(i=0;i<100;i++)
{}

python的for配合in使用,可以用在多个场合,比如:

str="你好,我是一个字符串"
for i in str:
    print(i,"",end="")
list=[1,2,3,4,5]
for j in list:
    print(j,"",end="")
for k in range(len(list)):
    print(list[k],"",end="")

输出结果为:

你 好 , 我 是 一 个 字 符 串 1 2 3 4 5 1 2 3 4 5 

这里讲解一下:

(1)python的forin 配合使用,in 必须在一个序列中,否则报错。

(2)字符串可以看做一个序列,列表当然是一个序列

(3)rangelen后面描述,range是最常见配合forin使用的关键字

for结合ifbreak关键字,可以实现跳出循环,for是一种迭代。

b、while

while和C语言里的写法就基本相似了

i=10
while i>0:
    print(i,"",end="")
    i-=1

输出结果为

10 9 8 7 6 5 4 3 2 1 

c、break

和C语言里的break相似,break是用来通过一个判断语句跳出循环

i=10
while i>0:
    print(i,"",end="")
    if i<=5:
        break
    i-=1

输出结果为

10 9 8 7 6 5 

 

可知,当if满足条件时,触发break,跳出整个循环

d、continue

contiune用来控制循环,当满足条件时,如果触发contiune,则调到循环的开头,continue以下的语句不执行

i=10
while i>0:
    print("第一次执行","",end="")
    i-=1
    if i<=5:
        continue
    print("第二次执行","",end="")

输出结果为:

第一次执行 第二次执行 第一次执行 第二次执行 第一次执行 第二次执行 第一次执行 第二次执行 第一次执行 第一次执行 第一次执行 第一次执行 第一次执行 第一次执行 

 

for i in range(10):
    print("第一次执行","",end="")
    if i>=5:
        continue
    print("第二次执行","",end="")

输出结果为:

第一次执行 第二次执行 第一次执行 第二次执行 第一次执行 第二次执行 第一次执行 第二次执行 第一次执行 第二次执行 第一次执行 第一次执行 第一次执行 第一次执行 第一次执行 

可知,continueforwhile语句中都可以实现跳转到循环起始位置,不执行后面语句的作用

python中的for语句和C不一样,不需要再对变量(如i)进行赋值操作(如i+=1

6、异常处理关键字

a、常规异常

异常处理是防止程序运行中出现异常错误的一种处理手段,涉及到的关键字通常包括tryexceptfinallyraise

我们先看一段代码:

print(1/0)

输出结果为:

ZeroDivisionError: division by zero

为了规避在程序中出现类似问题(比如写一个计算器,用户做除法时把分母输入成了0,如果不做处理,则程序中断运行),需要提前规避。

try:
    print(1/0)
except ZeroDivisionError:
    print("除数不能为0")

输出结果为:

除数不能为0

使用tryexcept,避免了程序出错,能保证程序的稳定运行。同很多关键字一样,需要在tryexcept后面加:并且内容缩进

except 指规避的错误,可以把异常名称列上(如上面的ZeroDivisionError)使程序根据针对性,也可以不加异常名称使所有错误都进行规避处理(无法精细处理),如:

try:
    print(1/0)
except:
    print("除数不能为0")

实际在代码的编写过程中,我们无法预知用户会出现什么错误,所以通常把异常名称列上分别处理。

except可以同时规避多个错误,用()圈起来,比如 except (异常): 的方式,当然,这些异常的处理结果都是其缩进的内容。

finally通常是用放置在except之后,表示即使有错误发生也要执行finally中的语句。

try:
    print(1/0)
except:
    print("除数不能为0")
finally:
    print("我会被显示出来")

输出结果为:

除数不能为0
我会被显示出来

finally可以不用except,仅仅结合try使用,如果这样使用,finally中的语句会被执行,然后报错(没有except

为了满足更多需求,python提供了一个引出异常的关键字,raise

try:
    a=10
    if a>9:
        raise ValueError
except ValueError:
    print("错误")

输出结果为:

错误

注意:

raise 只能引出一个在python异常表中的异常,如果自行定义异常名,则报错

raise 通常在try的缩进里使用,否则一开始就报错,程序终止

异常名可以进行自定义,单涉及到类和实例,此处不叙

b、特殊异常

python还提供了assertwith两个特殊的异常处理,暂时不叙

7、函数关键字

和众多的编程语言一样,python提供函数式编程方法,涉及的关键字主要包含:defreturn

def firstapp(x,y):#first为函数名,自定义;x、y为参数
    pass#占位符,不执行操作
    return x+y #返回x+y的值
a=firstapp(1,2) #执行函数,并把返回值赋值给a
print(a)#打印a

输出结果为:

3

在上面的代码中,pass表示一个占位符,不执行任何操作,return表示函数返回一个数据,a=firstapp(1,2)表示把这个数据赋值给a,定义的函数里可以设置参数,在上面代码中为x,y

注意:

如果函数没有返回一个数值,强制赋值给一个变量时,则变量的数据类型为None

以上面的代码为例,firstappfirstapp()是两种数据类型,firstapp是函数,firstapp()是返回值,切记!

def firstapp(x,y):
    pass
    return x+y
secondapp=firstapp
a=secondapp(1,2)
print(a)

从上面代码可以知道,函数也是一种数据类型,可以赋值给另外一个变量

如果函数中想要对全局变量(函数外的变量)进行操作,需要在变量前添加关键字 global,否则和预期的结果不一致,比如:

a=0
def test1():#定义一个叫test1的函数
    a=2#函数里试图修改a的值
test1()#定义完函数之后需要执行函数
print(a)#打印a

输出结果为:

0

之所以出现上面的结果,是因为在函数中定义的变量通常都只在函数中生效,上述代码中函数中的a=2实际是新建了一个临时变量a,若想修改全局变量:

a=0
def test1():#定义一个叫test1的函数
    global a#我要修改全局变量a
    a=2#函数里试图修改a的值
test1()#定义完函数之后需要执行函数
print(a)#打印a

输出结果为:

2

注意,global关键字不能 global a=2 这样一次性赋值,需要先声明,后赋值,否则报错

另外一个和global关键字比较接近的是 nonlocal,其用途和global类似,但作用域不是全局变量,而是外层变量

def test1(): #定义一个函数
    a=0 #定义函数内的一个变量,名字叫a
    def test2():#在函数中定义一个新函数
        nonlocal a #声明这个a不是新函数里面的
        a=2#对a的值进行修改
    test2()#嵌套的函数也同样需要定义后执行
    print(a)#打印a的值
test1()#定义好的函数执行

输出结果为:

2

nonlocal是不能修改全局变量的,否则报错

从上面的代码可以看出,函数本身可以嵌套另一个函数。实际上,函数的参数也可以是函数,函数还可以递归,函数还可以返回一个对象等等,这些内容在第二部分讲解。

8、lambda

如果我们临时需要一些简单的函数,用def来实现不太灵活,python提供了lambda关键字供我们灵活使用

def add(x,y):
    return x+y
print(add(5,6))

输出结果为

11

对应上面的函数,我们可以调整为:

add=lambda x,y:x+y
print(add(1,2))

也可以调整为这样:

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

相比def而言,本质上没有区别,可灵活应用

9、类关键字

a、想象

类是面向对象的一种方法,非常神奇,也非常重要,其关键字为class

“类”可以理解为是一种“模板”,比如“书”,根据模板可以创建无数个“对象”,比如语文书、数学书、小说等。

这些“对象”有着很多相似的地方,如果定义了“类”,即“模板”后,那么这些“对象”有着很多共同的特征,这些特征可以是“方法”,可以是“属性”,特征的具体数值不一样,但“对象”都拥有这些特征。

我们以一个表格为例:

实例名(对象名)名称页数色彩改页改名改色
语文书语文100黑白
数学书数学120黑白
漫画书漫画200彩色

① 上表中,语文书、数学书、漫画书称为“实例”,也称为“对象”,他们都是根据“类”(可以理解为“模板”)创建的

② 表中的“名称、页数、色彩”称为“实例”(“对象”)的属性(不是“类”的属性)

③ 表中的“改页、改名、改色”称为“实例”(“对象”)的方法,实际上方法是函数

④ 语文书、数学书、漫画书都是书,书就是他们的模板名(类名)

⑤ 书(“类”)可以拥有自己的属性和方法,“实例”(“对象”)可以访问他们

b、实践

class book:#和函数不同,class类名的后面不需要(),传参通过下面的构造函数
    def __init__(self,name,page,color):#__inti__称为构造函数,在生成实例时自动执行
        self.name=name#self表示实例(对象),self.name表示将参数里的name传给对象,使其新增一个属性叫做name(self.name)
        self.page=page#self.name和name中,除了self不建议变之外,剩下的任意选择,跟着self的name是对象的属性
        self.color=color#对象的属性在调用时访问 对象.属性的方式来实现
    def changenamemethod(self,newname):#对象的方法,用来修改名称
        self.name=newname#赋值对象的名称属性一个新名称
    def changepagemethod(self,newpage):#对象的方法,用来修改页数
        self.page=newpage#赋值对象的页数属性一个新页数
    def changecolormethod(self,newcolor):#对象的方法,用来修改颜色
        self.color=newcolor#赋值对象的颜色属性一个新颜色
math=book("数学",100,"pw")#生成另一个对象(实例),对象名字叫math,其名称属性叫“数学”
chinese=book("语文",80,"pw")#生成一个对象
manga=book("漫画",120,"red")#生成一个对象
print(math.name,math.page,math.color,chinese.name,chinese.page,chinese.color,manga.name,manga.page,manga.color)#打印对象的属性
math.changenamemethod("高中数学")#调用对象的方法
math.changepagemethod(120)#调用对象的方法
math.changecolormethod("yw")#调用对象的方法
print(math.name,math.page,math.color,chinese.name,chinese.page,chinese.color,manga.name,manga.page,manga.color)#打印对象的属性

输出结果为:

数学 100 pw 语文 80 pw 漫画 120 red
高中数学 120 yw 语文 80 pw 漫画 120 red

 

可以知道,对象(实例)拥有属性(变量)和方法(函数),其中,__init__称为构造方法,在生成一个对象的过程中,如果构造方法里有参数,那么对象的生成过程中必须要有参数,同时构造方法自动执行

除了对象拥有属性和方法外,类(模板)本身也有属性和方法,这里简单说一下类的属性:

class book:
    count=0 #类的属性,可以共同访问
    def __init__(self,thename,thepage):
        self.name=thename
        self.page=thepage
        book.count +=1 #因为构造函数自动执行,这里让类的属性+1
    def pageadd(self):
        self.page +=1
    def changename(self,str):
        self.name=str
print(book.count) #看初始化类的属性
math=book("数学",100)#生成一个对象
chinese=book("语文",120)#生成一个对象
print(book.count)#再看类的属性

输出结果为

0
2

可以看到,构造函数自动执行并对类的属性进行了修改。

10、模块关键字

python的一个优点就是有各种各样成品模块供我们使用,分为IDLE自带的模块及第三方模块

想使用其中一个模块,需要先导入进来,再利用模块中的函数,比如我们要使用random模块中的randint函数

import random #最简单的方法,导入模块
print(random.randint(1,10))#使用模块中的函数,需要通过模块名.函数调用
import random as r #为了减少输入,可以把模块自定义一个名称
print(r.randint(1,10))#通过自定义名称.函数的方式调用
from random import randint #也可以只导入模块中的一个函数
print(randint(1,10))#直接利用函数,不需要模块名,但注意可能会和自己的函数名冲突
from random import * #可以导入所有函数,直接使用
print(randint(1,10))#不需要模块名,但要注意冲突
from random import randint as r#导入其中某个函数的同时还可以给其自定义
print(r(1,10))#调用自定义的函数

输出结果是5个随机数

从上面可以知道importfromas的用法

11、其他关键字

del用来删除一个数据的指向或者删除一个列表中的元素

a=1
b=a
print(id(a),id(b))
del a
print(b)
print(id(b))

输出结果为:

1352597968 1352597968
1
1352597968

可知,对于一个数字型数据类型,del删除的是指向,不是数据本身

我们试一试列表

list=[1,2,3]
del list[0]
print(list)

输出结果:

[2, 3]

yield关键字比较复杂,暂时不叙

第二部分:进阶

一、函数的参数

1、必选参数、默认参数

def power(x,n=2):
    return x**n
print(power(5))
print(power(5,10))

输出结果为:

25
9765625

在上述代码中,x为必选参数,必须输入。n叫默认参数,如果不输入就采取默认值,比如power(5)因为默认n=2,所以可以不输入n

默认参数必须在必选参数之后

默认参数的值不要指向一个变量(否则下次运算时的默认值已经发生了变化)

def tmp(x,y):
    print("第一个参数是",x)
    print("第二个参数是",y)
tmp(1,2)
tmp(y=1,x=2)

输出结果为:

第一个参数是 1
第二个参数是 2
第一个参数是 2
第二个参数是 1

由上可知,必选参数的函数也可以通过参数赋值的方式来进行参数传递

有默认参数的函数在传参过程中,有多种方式,注意有一种错误如下:

def tmp(x,y=1,z=1):
    print(x+y+z)
tmp(1,2,3)
tmp(1,z=2,y=3)
tmp(1,z=2)
tmp(1,y=2)

输出结果为:

6
6
4
4

但如果是这样:

def tmp(x,y=1,z=1):
    print(x+y+z)
tmp(1,y=2,3)

会报错:

SyntaxError: positional argument follows keyword argument

即:可选参数y=2后不能接一个必选(任意数字)形式的表达,要么不写(采用默认参数),要么用z=数字的方式

2、*多个参数(可变参数)

当不知道函数的参数数量时,python支持多个不确定数量参数传入,用*标记

def test(x,*y):
    print("你输入的参数是:")
    print(x)
    for i in range(len(y)):
        print(y[i])
test(10,"a","b")

输出结果为:

10
a
b

*传入的参数实际上被作为一个元组来对待,所以在函数内可以对其进行相关操作

def test(x,*y):
    print("你输入的参数是:")
    print(x)
    print(y)
test(10,"a","b","c")

输出结果为:

10
('a', 'b', 'c')

也可以在函数外定义一个元组,在函数里调用

a=(1,2,3)
def test(x,*y):
    print("你输入的参数是:")
    print(x)
    print(y)
test(5,*a)

输出结果为:

5
(1, 2, 3)

注意,函数调用时,a前面需要附加*号,表示a作为参数y的元组。如果没有*号,则把a本身作为元组的一个元素来使用。

a=(1,2,3)
def test(x,*y):
    print("你输入的参数是:")
    print(x)
    print(y)
test(5,a)

输出结果为:

5
((1, 2, 3),)

3、**关键字参数

和可变参数不同,关键字参数在调用函数时,参数赋值需要用默认参数的方式传入,比如:

def tmp(x,**y):
    print(x,y)
tmp(1,2)

输出结果为:

TypeError: tmp() takes 1 positional argument but 2 were given

应该为:

def tmp(x,**y):
    print(x,y)
tmp(1,b=2)

输出结果为:

1 {'b': 2}

可知,关键字参数是将**号后面的内容作为一个字典来传入,所以调用需要默认参数的方式,如上面的b=2b作为字典中的“键”,2作为其对应的“值”

二、函数的高级用法

1、参数可是函数

python里面的函数定义过程中,其参数可以包含另一个函数

def amazing(func,x):
    return func(x)#即便power返回,amzing如果无返回其数值为None
def power(x):
    return x**2#返回数据
print(amazing(power,6))

输出结果为:

36

在上面例子中,amazing可以调用任何函数,不止是power,只要是一个参数的函数,都是可以调用(在上例中,参数多则报错)

在上面的函数中,先执行power函数,把其返回值返给amazing函数,amazing函数再生成一个返回值返给print函数

和2-7章节需注意的事项一样,对于一个函数来说,()是先执行函数,如果有返回值则返回,没有返回值返回None,函数名本身代表函数,函数名+()代表的是返回值

2、递归

我们实现一个阶乘来举例,应用普通函数法:

def mu(n):
    result=n
    for i in range(n):
        if n>1:
            result=result*(n-1)
            n-=1
    print(result)
mu(10)

输出结果为:

3628800

n=n-1这种应用非常方便,在运行时对n进行操作后再赋值给n,不需要临时变量存储n-1再赋值给n,上面代码的result存储最终值,然后对其进行更改后再进行新的赋值

如果应用递归,则有:

def mu(n):
    if n==1:
        return n
    else:
        return n*mu(n-1)
print(mu(10))

输出结果为:

3628800

在上述代码中,对于n==1的情况进行了特别的处理,用以确定一个出口

递归必须有一个出口,否则会无限循环,达到递归深度时报错

递归相比循环而言,占用性能较高,应谨慎使用(上述代码中影响不大)

通常情况下,递归相比循环,代码更简练

3、返回值

假设定义了一个函数,名为func,函数没有参数,有:

1、func表示函数本身,func()表示执行这个函数

2、有一个变量aa=func表示把func函数赋值给aa变成了函数,a()执行功能等同func()

def func():
    print("hello")
a=func
func()
a()

输出结果为:

hello
hello

3、a=func()表示先执行func(),再把func函数的返回值给a,在本例中,func函数没有返回值,则a为一个None

def func():
    print("hello")
a=func()
print(a)

输出结果为:

hello
None

可以看到,在a=func()的过程中,func函数已经执行、func()并没有一个返回值,所以a为None

def func(x):
    return x
a=func(5)
print(a)

上例中,func函数有返回值,为其输入的参数,所以a=func(5)的过程中,先执行func(5),再把返回值给aa可以正常打印。

输出结果为:

5

4、函数不仅仅可以返回一个普通数值,也可以返回一个函数本身

def func():
    print("执行func第一次输出")
    def func2(x):
        print("func内嵌函数第二次输出")
        return x
    return func2
a=func()
print(a(5))

输出结果为:

执行func第一次输出
func内嵌函数第二次输出
5

上述代码中,a=func(),表示先执行func函数,将其返回值func2(函数)赋值给a,所以a也是一个函数,可以通过a(参数)的方式执行

5、小结:

​ a、func表示函数,func()表示执行(即便没有赋值给一个变量)

​ b、a=funca是一个函数;a=func()afunc函数的返回值

​ c、返回值可以是一个函数本身

三、类的进阶(面向对象)

1、基础

class book:
    def __init__(self,name):
        self.name=name
    def changename(self,name):
        self.name=name
    def printname(self):
        print("书名是",self.name)
book1=book("数学")
book1.printname()
book1.changename("语文")
book1.printname()

输出结果为:

书名是 数学
书名是 语文

解析:

a、__init__称为构造函数,在对类进行实例化,形成一个对象时,自动执行__init__函数部分,传参数通过构造函数进行,所以class 类名: 后不需要()传参。

​ 除非生成的对象不需要参数,否则必须有构造函数进行参数传入

b、self表示“对象”,def __init__(self,name):和普通函数的 def func(name):相比,在类中需要传入self表示是对“对象”的操作,比如上面的self.name=name,表示把name参数赋给对象的name属性

​ 对象的属性在其类的任意一个函数中都可以访问

c、self.name,表示name是生成的对象的一个属性,属性名为name,函数changenameprintname表示对象的一个方法,属性和方法都通过对象.属性名(方法名)调用,区别是属性不需要(),方法因为是函数,需要()

​ 对象的属性和方法的名称不能相同

2、继承

a、继承的方式

class book:
    def __init__(self,name):
        self.name=name
    def changename(self,name):
        self.name=name
    def printname(self):
        print("书名是",self.name)
class mange(book):
    def __init__(self,name,color):
        self.name=name
        self.color=color
    def printpage(self):
        print("漫画书的颜色是",self.color)
manga1=mange("火影","彩色")
manga1.printname()
manga1.printpage()

输出结果为:

书名是 火影
漫画书的颜色是 彩色

分析可知:

a、父类的printnamechangename函数可以传承给子类,子类不需要再进行定义

b、如果想添加参数,构造函数需要重新定义,否则无处传参

c、如果继承的方法中包含父类的参数,则子类也需要定义构造函数,将其包含父类参数,否则执行继承的方法时找不到相关属性

对于父类有构造函数的类来说,子类继承时,构造函数中的参数可以按照上例一样分别列出self.name=name

self.color=color,当父类构造函数中参数过多时,有简化方法可以使用

class mange(book):
    def __init__(self,name,color):
        book.__init__(self,name)
        self.color=color

通过类内部调用构造函数的方式,实现 参数的传递,减少参数赋值工作量

也可以采用如下方式:

class mange(book):
    def __init__(self,name,color):
        super(mange,self).__init__(name)
        self.color=color

采用super方式能避免当一个子类需要变更父类时的混淆产生。

要注意的是:采用super时,self的位置有所变化

python3中,super(mange,self).__init__(name)可以改为super().__init__(name)

应注意的是:常规情况下,无论是哪种继承方式,子类构造函数的声明、参数的传入要执行,在构造函数内部可以灵活调整赋值方法(上例中def __init__(self,name,color)必须有,否则无处传参)

 

除了上面的继承方式外,有一种继承可以通过直接定参的方式进行:

class tmp1:
    def __init__(self,name):
        self.name=name

class tmp2(tmp1):
    def __init__(self):#这里不传入参数了,通过父类构造函数指定参数为固定值
        tmp1.__init__(self,"hello")

a=tmp2()
print(a.name)

输出结果为:

hello

可知,在tmp2继承tmp1时,通过调用tmp1的构造函数,已经把所有tmp2生成的对象的名称进行了固定,为hello,相当于指定了tmp2类生成的所有对象的名称。

也可用super(tmp2,self).__init__("hello") 或者super().__init__("hello")来指定参数

b、多重继承

python的多重继承执行MRO,通过C3算法实现,难度大,此处不叙

3、装饰器

在说装饰器前,有一个有趣的地方需要先了解

class test:
    def __init__(self):
        print("我是构造函数")
test()

输出结果为:

我是构造函数

上面的例子说明,生成一个实例并不必须通过a=test()的方式实现,直接调用类名(),实际上已经生成了一个实例,a=test()是先生成一个实例再赋值给a

从上面代码可知,单纯 test()可以是一个实例,可以是一个函数,从外表看起来很相似,后面单独列章节将类和函数进行比较。

因为实例的生成不需要通过=来实现,所以调用某个实例的方法可以这样:

class test:
    def __init__(self):
        print("我是构造函数")
    def method(self):
        print("我是一个方法")
test().method()

输出结果为:

我是构造函数
我是一个方法

注意,这里要用test().方法去调用,也就是调用实例的方法。

注意,a=test()是生成一个实例a,如果a=test().method(),则是把method方法的返回值给了a

那么,除了实例的方法外,还有其他哪几种方法呢?

a、类方法

class test:
    name="test1"
    @classmethod
    def classmethod(cls):
        print(cls.name)
test.classmethod()

输出结果为:

test1

类方法调用和前面实例方法调用不同,类方法调用不需要通过()生成实例的方式来调用,直接用类名调用即可。

类方法可以访问类的属性并对其进行操作,当然也可以在其中间生成一个对象并进行相关操作

class test:
    name="test1"
    def method(self):
        print("普通方法")
    @classmethod
    def classmethod(cls):
        print(cls.name)
        test().method()
test.classmethod()

输出结果为

test1
普通方法

 

上面代码中,name是类test的属性,可以通过类.属性来访问,类属性可以被对象共享,所以也可以通过对象.属性访问类的属性

class test:
    name="test"
    def __init__(self):
        pass
a=test()
print(a.name,test.name)

输出结果为:

test test

但是,通过对对象.属性的方式来访问类属性可以,但不能修改,因为对象.属性可以在对象外访问,所以如果通过对象.属性的方式修改,实际是给对象增加了一个同名的属性,类属性并没有变化

class test:
    name="test"
    def __init__(self):
        pass
a=test()
print(a.name,test.name)
a.name="hello"
print(a.name,test.name)

输出结果为:

test test
hello test

类和对象的外部可以访问并修改其属性、方法

非构造函数的其他方法同样可以增加属性

因为类方法有cls参数,可以返回一个实例

class test:
    @classmethod
    def classmethod(cls):
        return cls()
    def method(self):
        print("普通方法")
a=test.classmethod()
a.method()

输出结果为:

普通方法

可知,上述代码中a=test.classmethod()表示把类方法的返回值(此处是一个实例)给了一个变量a

目前为止,关于x=y()的表现方式出现了很多次,我们后面总结下其几种方式

类方法可以共享给实例来使用:

class test:
    @classmethod
    def classmethod(cls):
        print("类方法")
    def method(self):
        print("普通方法")
test().classmethod()
test.classmethod()

输出结果为:

类方法
类方法

b、静态方法

除了动态方法外,通过类名.方法的型式访问的还有一种,叫静态方法

class test:
    @staticmethod
    def smethod():
        print("我是个静态方法")
test.smethod()

输出结果为:

我是个静态方法

访问静态方法的方式和类方法相似,通过类名.方法的方式进行访问,静态方法是一个普通函数,没有self,没有cls,所以不能访问类变量和实例变量。

同类方法一样,实例可以访问静态方法

class test:
    @staticmethod
    def smethod():
        print("我是个静态方法")
test.smethod()
test().smethod()

输出结果和上面代码相同

d、共同点和区别

前面讲了普通方法、类方法、静态方法三种不同的方式,其共同点与区别如下:

相同点:

1、都可以通过实例.方法的方式来调用

不同点:

1、类方法拥有cls参数,可以实现类变量的相关操作

2、普通方法拥有self参数,可以实现实例变量的相关操作

3、静态方法没有类及实例的参数,不能访问类及实例的变量,但可以用普通参数实现非类和实例相关运算

四、函数与类

1、x=y()

对于x=y()这种方式,前面经过了多次介绍,我们先总结一下:

对于函数来说:

1、x=y()表示先执行y(),再把返回值给x,所以单独的y()表示执行不赋值(返回值没有赋值)

​ (if调用同样也是执行再验证返回值,一个函数的参数里调用也同样先执行后返回值)

def test(x):
    print(x)
    return x
if test(True):
    print("这句话可以被显示")
#--------------------
def test2(x):
    print(x,x)
test2(test("hello"))

输出结果为:

True
这句话可以被显示
hello
hello hello

2、返回值要求有return,如果强行赋值一个没有返回值的函数yx,则x的类型为None

3、y为函数本身,类型为function,y()的类型为其返回值类型,y()实际上是其返回值

def tmp():
    pass
print(type(tmp()))
print(type(tmp))
def tmp2():
    return 5
print(type(tmp2()))
print(type(tmp2))

输出结果为:

<class 'NoneType'>
<class 'function'>
<class 'int'>
<class 'function'>

当然,即使y()没有返回值,其改执行的语句正常执行。

那么,代码中x=y()的寓意就表示把y函数的返回值给x了嘛?并不是,因为还有类,类可以生成实例,实例可以有方法,方法可以有返回值

对于类来说:

x=y(),如果y是一个类,那么y()表示类的一个实例(没有参数),x为一个实例

如果y里有构造函数,那么x=y()的过程中,构造函数自动执行,从表面看起来和函数很像

class y:
    def __init__(self):
        print("我是构造函数")
x=y()

def y():
    print("我是一个函数")
x=y()

输出结果为:

我是构造函数
我是一个函数

2、奇妙之处

a、都执行函数

class cla:
    def __init__(self):
        print("我是一个构造函数")
def func():
    print("我是一个普通函数")

cla()
func()

输出结果为:

我是一个构造函数
我是一个普通函数

之所以相似,是因为类()就是一个实例,实例自动执行构造函数,而func()表示执行函数,所以本质上都是函数的执行。

b、都执行方法

class cla:
    def method(self):
        print("我是一个实例的方法")
def func():
    class tmp:
        def method(self):
            print("我是一个函数的返回值,返回的是一个实例")
    return tmp()

cla().method()
func().method()

输出结果为:

我是一个实例的方法
我是一个函数的返回值,返回的是一个实例

之所以相似,是因为类()就是一个实例,实例自动执行构造函数,所以cla().method()表示实例的一个方法

func()表示执行函数并且返回,而返回值实际上是一个实例,所以其拥有方法

可以把func()理解成是其返回值,在返回之前先执行

当然,也可以这样:

class cla:
    def method(self):
        print("我是一个实例的方法")
def func():
    class tmp:
        def method(self):
            print("我是一个函数的返回值,返回的是一个实例")
    return tmp()

a=cla()
b=func()
a.method()
b.method()

a=cla()表示一个实例,b=func(),因为func()返回值是一个实例,所以b也是一个实例

至此,关于返回值,func,func(),class,class(),class.method(),class().method(),func().method都有了介绍,对于函数func.method()目前尚没有发现其可行性。

小节

func 函数,func() 返回值,需要先执行

class 类,class()实例

class.method() 类方法或者静态方法

class().method() 实例方法

func().method() 通过返回实例的方法调用实例方法

五、一切皆对象

1、数据类型

我们知道,python中有很多自带的方法可以利用,比如列表:

a=[]
a.append("hello")
print(a)

输出结果为:

['hello']

之前很是疑惑,为什么列表可以拥有一个方法呢?

实际上,在python中,所有的数据类型都是实例。

print(type(int),type(float),type(str),type(list),type(tuple),type(set),type(dict))
print("数据的类型的类型都是类-。-")
print(type(5),type(5.0),type("hello"),type([1,2]),type((1,2)),type({1,2}),type({"a":1}))
print("但看实际数据的类型都是对应的类型-。-")
print(isinstance(0,int))
print(isinstance(5.0,float))
print(isinstance("hello",str))
print(isinstance([1,2],list))
print(isinstance((1,2),tuple))
print(isinstance({1},set))
print(isinstance({1:1},dict))

输出结果为:

<class 'type'> <class 'type'> <class 'type'> <class 'type'> <class 'type'> <class 'type'> <class 'type'>
数据的类型的类型都是类-。-
<class 'int'> <class 'float'> <class 'str'> <class 'list'> <class 'tuple'> <class 'set'> <class 'dict'>
但看实际数据的类型都是对应的类型-。-
True
True
True
True
True
True
True

可以看出,int、float、str等都是python中固定默认的类名,具体数据是它们的实例

既然它们是类,那我们继承下

class newlist(list):
    pass
a=newlist([1,2,3])
print(a)
a.append(6)
print(a)

输出结果为:

[1, 2, 3]
[1, 2, 3, 6]

可以看出,newlist继承了list后,同样拥有list的append方法。

注意,在上面代码中,a=newlist([1,2,3]),参数是一个列表而不是多个数字,和普通a=(1,2,3)的方式不同。

2、更多

到这里,手册暂时完成,以后进行更新、补充