Python – 基本语法
你会发现这篇文章长得离谱,看看目录你就知道了。
目录
python是使用缩进来区分不同的代码块,所以对缩进有严格要求
逻辑行:Python解释器对代码进行解释,一个语句是一个逻辑行。
1、逻辑行的“首行”需要顶格,即无缩进(也就是一份源码的第一个逻辑行)
Python可以使用空格或制表符(tab符)标记缩进。缩进量(字符个数)不限
filter(function or None, sequence)
缩进原则
python是使用缩进来区分不同的代码块,所以对缩进有严格要求
1、缩进不符合规则,解析器会报缩进错误,程序无法运行。
2、缩进的不同,程序执行的效果也有可能产生差异。
例如图中的代码,左边代码会打印第2行,而右边代码,1,2行都不打印
代码缩进规则
缩进是针对逻辑行的,因此首先要区分代码中的物理行和逻辑行。
物理行:代码编辑器中显示的代码,每一行是一个物理行。
逻辑行:Python解释器对代码进行解释,一个语句是一个逻辑行。
python代码中
1、可以使用";"
号将多个逻辑行合并成一个物理行。
**这里有个前提,多个逻辑行应该属于同一个代码层级。
像这种分支else代码块就不能直接使用“;”连接了。
if True:a=1; else: a=2
2、可以使用"\"
对一个逻辑行进行换行,书写为多个物理行。
3、字典、列表等变量赋值语句,是可以直接换行,书写为多个物理行的。
缩进规则
1、逻辑行的“首行”需要顶格,即无缩进(也就是一份源码的第一个逻辑行)
2、相同逻辑层(同一个代码块)保持相同的缩进量
3、":"
标记一个新的逻辑层
如:while循环、if分支、函数声明、类定义等等
缩进量及缩进字符
Python可以使用空格或制表符(tab符)标记缩进。缩进量(字符个数)不限
而空格
和tab符
通常都以空白形式显示,混用不容易区分,影响代码阅读,增加维护及调试的困难。因此 Python PEP8 编码规范,指导使用4个空格作为缩进。
实际开发,代码规模较大,缩进深度的影响,会选择2个空格做为缩进,更易于阅读。
函数
函数定义
函数概述
在程序设计中,函数的使用可以提升代码的复用率
和可维护性
。
提升代码的复用率: 程序设计中,一些代码的功能是相同的,操作是一样的,只不过针对的数据不一样。此种情况下,可以将这种功能写成一个函数模块,要使用此功能时只需调用这个函数模块就可以了。
提升代码的可维护性: 使用函数后,实现了代码的复用,某个功能需要核查或修改时,只需要核查或修改此功能相对应的函数就可以了。对功能的修改可以使调用该函数的所有模块同时生效,极大提升了代码的可维护性。
内建函数
:内建函数也叫内置函数,即系统已经定义好的函数,开发者可以直接调用。为了使开发者对内函数
和自定义函数
有一个直观的认识,下面给出一个简单示例。
调用系统内建函数pow():
pow(2, 4)
自定义函数func():
def func(a, b):
return a ** b
func(2, 4)
上述代码中,首先调用了Python语言的内建函数pow()进行幂运算;
然后,自定义了一个函数func(),功能是输出a的b次幂;最后调用了自定义函数func(),输出相应的结果。可以看出,Python语言中函数的定义和使用都是非常便捷的。
函数的定义
在Python语言中,函数通常是由函数名、参数列表以及一系列语句组成的函数体构成的。函数定义的一般格式如下:
def 函数名(参数列表):
函数体
例如:
def hello():
print("hello")
print("world!")
以上实例定义的hello()函数虽然不包含任何参数,但是函数名后的一对括号
是不能省略
的。在实际应用中,稍复杂的函数通常都会包含一个或多个参数。
下列代码定义了一个计算矩形面积的函数area()和一个欢迎信息打印函数welcome()。
# 计算矩形面积的函数area()
def area(width, height):
return width * height
# 输出汉英信息的函数
def welcome(name):
print("Welcome ", name)
# 调用welcome函数
welcome('张三')
# 调用area函数
w = 4
h = 9
print("with=", w, "height=", h, "area=", area(w, h))
上述代码中,首先定义了area()和welcome()两个函数,其中函数area()提供了width(宽)和height(高)两个参数,函数welcome()函数只提供了一个参数name。然后,分别调用了area()和welcome()函数,在控制台输出了相应的结果,结果如下:
Welcome 张三
with= 4 height= 9 area= 36
以下代码定义了无任何操作的空函数nop()。
def nop():
pass
在Python代码中,pass语句通常可以用来作为占位符,表示什么操作都不执行。比如在项目起始阶段,如果还没想好函数具体实现时,可以先放置一个pass语句,让代码先成功运行起来。待项目框架搭建完毕后,在进行相应的具体实现。
通常情况下,在Python语言中定义一个具有特定功能的函数需要符合以下规则:
形参和实参
在编程语言中,函数定义时用的是形参,调用时用的是实参
形参(parameter)
,全称为"形式参数"
,不是实际存在的变量,又称虚拟变量。形参是在定义
函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。
实参(argument)
,全称为"实际参数"
,是在调用
时传递给函数的参数。实参可以是常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。
形参和实参的功能是数据传送。
在调用函数时,实参将赋值给形参。必须注意实参的个数、类型应与形参要一一对应
,并且实参必须要有确定的值
。形参的作用域一般仅限函数体内部,而实参的作用域根据实际设置而定。
以计算面积的函数为例:
# 计算矩形面积的函数area()
def area(width, height):
return width * height
# 调用area函数
w = 4
h = 9
print("with=", w, "height=", h, "area=", area(w, h))
上述代码中,函数area()定义处的width和height就是形式参数,函数体外定义的变量w和h是实际参数。可以看到,把实参w和h传入函数体后,就把相应的值赋值给了形参width和height。形参width和height的作用域只限于area()函数体内,而实参w和h作用域则根据外部调用处的设置而定。
对于函数形参列表,默认情况下函数调用时的参数值与参数列表声明中定义的顺序是一致。Python语言也允许函数调用时参数顺序与声明时不一致,即显示指明关键字参数,并根据参数的指定进行赋值。
def func(x, y):
print('x+y=', x + y)
print('x*y=', x * y)
# 等效于func(x=3,y=2),也等效于func(3,2)
func(y=2, x=3)
上述代码中,函数func()定义时形式参数的顺序是func(x, y),但是调用时实际参数的顺序确是func(y = 1, x = 2)。这是因为Python语言中提供了一种关键字参数的机制,可以给开发者提供更大的灵活性。
函数的返回值
函数的返回值是函数执行完成后,系统根据函数的具体定义返回给外部调用者的值。
在实际开发中,有时不仅仅要执行某个函数的功能,而且还需要把该函数的执行结果作为其他函数或功能的计算单元。所以,函数返回值是非常有用的
在Python语言中,当函数运行到return语句时即执行完毕,同时将结果返回。因此,可以在函数内部通过条件判断和循环设置实现较复杂的逻辑,并返回预期的结果。如果没有return语句,函数体内所有语句执行完毕后默认返回None。
# 函数定义
def add(x, y):
print('x+y=', x + y)
return x + y
# 函数调用
result = add(y=1, x=2)
print(result)
上述代码中,定义的add()函数返回“x+y”的运算结果。可以看到,调用该函数后,把该函数的返回值赋值给了变量result ,最后输出了变量result 的值。
另外需要注意的是,在Python语言中,函数也可以有多个返回值,例如:
# 函数定义
def add(x, y):
print('x+y=', x + y)
print('x*y=', x * y)
return x + y, x * y
# 函数调用
a, b = add(y=1, x=2)
print(a, b)
上述代码中,定义的add()函数有连个返回值,分别是“x+y”和“x*y”。可以看到,调用该函数后,把该函数的返回值分别赋值给变量a,b,最后输出了变量a和变量b的值。
注意: 返回值和接收变量的对应关系,是按照顺序一一对应的
函数分类
2.2.1 内置函数
Python语言中自带的函数叫做内建函数,这些内建函数对大部分常用操作进行有效封装,可以直接调用,为开发提供了极大便利。由于内建函数是Python语言内置的函数,因此不需要导入任何函数库即可直接调用,常用的内建函数如图所示。
在Python语言中,除内建函数外的其他类型函数通常被称为第三方函数。
以下代码演示了内建函数abs()的调用过程及内建函数max()的调用。
abs(100)
abs(-10)
max(1, 2)
max(-2, 0, 4, 1)
从上述代码可以看出,内建函数max()可以同时返回多个数值的最大值,而其他编程语言中的类似函数一般只能接收两个变量。Python语言中内建函数的功能强大可见一斑。
Python语言常用的内建函数还包括数据类型转换函数,以下代码演示了常用类型转换函数的方法。
print("int('12'):", int('12'))
print("int(12.3):", int(12.3))
print("float('12.3'):", float('12.3'))
print("str(1.23):", str(1.23))
print("str(10):", str(10))
print("bool(1):", bool(1))
print("bool(''):", bool(''))
输出结果:
int('12'): 12
int(12.3): 12
float('12.3'): 12.3
str(1.23): 1.23
str(10): 10
bool(1): True
bool(''): False
上述代码中,分别演示了内建函数int()、float()、str()和bool()的使用方法。其中,int()函数是把传入的参数转换为整数类型,float ()函数是把传入的参数转换为浮点类型,str()函数是把传入的参数转换为字符串类型,bool()函数是把传入的参数转换为布尔类型。
在Python语言中,还可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”,如下代码所示。
a = abs
print(a(-1))
这里需注意,abs没有小括号,因此加了小括号相当于调用函数了。Python语言中提供内建函数还有很多,由于篇幅限制,在此不一一列出。内建函数功能强大,理解并熟练掌握能较大提升开发效率。
自定义函数
当内建函数不能满足要求时,开发者可以根据实际需要自定义函数。函数自定义完成后,开发者可以在其他代码处通过函数名调用。如下代码演示了自定义函数printme()的定义和调用过程。
# 自定义函数
def printme(str):
"函数功能:打印传入的字符串"
print(str)
# 调用自定义函数
printme("调用用户自定义函数!")
printme("再次调用用户自定义函数!")
输出结果:
调用用户自定义函数!
再次调用用户自定义函数!
上述代码中,自定义了一个函数printme(),并对其进行两次调用,测试相应功能。在实际开发中,涉及到大量的自定义函数。在自定义函数中,也可以调用内建函数或其他自定义函数。自定义函数和内建函数的定义方式是相同,只不过是自定义函数是有开发者定义的,而内建函数是由系统定义的。两者的调用方式都是一样的。
在Python语言中,内建函数可以直接使用,第三方函数需要使用import命令导入相应的库才能使用。对于自定义函数,其定义和调用可以在同一个文件中,也可分离成不同的文件。
from test import hello
hello()
上述代码演示了函数的定义和调用不在一个文件的情形。首先,将hello()函数定义好并保存为test.py文件,然后使用Python语言的import指令“from test import hello”将该文件导入,可以调用hello()函数了。导入时需要注意test是文件名并且不含.py扩展名。
函数参数
参数种类
函数参数分为可变类型和不可变类型,其调用结果是不同的。
(1)可变类型:类似c++的引用传递,如列表、字典等。如果传递的参数是可变类型,则在函数内部对传入参数的修改会影响到外部变量。
(2)不可变类型:类似c++的值传递,如整数、字符串、元组等。如果传递的参数是不可变类型,则在函数内部对传入参数的修改不会影响到外部变量。
不可变类型参数实例:
def change_int(a):
a = 10
b = 2
change_int(b)
print(b) # 结果是2
上述实例中,有int类型的对象2,指向它的变量是b。在传递给change_int()函数时,按传值方式复制了变量b,a和b都指向了同一个int对象。在a=10时,则新生成一个int值对象10,并让a指向它。
一个更详细的例子,用id打印出变量的内存地址,可以看出函数内核函数外的内存发生了变化,说明用的是不同的内存单元,存放不同的数据:
可变类型参数实例:
def change_int(my_list):
"修改传入的列表"
my_list.append([1, 2, 3])
print("函数内修改后的变量:", my_list)
my_list = [10, 20, 30]
change_int(my_list)
print("函数外变量的值:", my_list)
在调用函数时,如果传入的参数是可变类型,则外部变量也会被更改。在上述例子中,传入函数的list对象和在末尾添加新内容的mylist对象用的是同一个引用。
从下图中内存地址可以看出,变量的内存地址没有发生变化,说明是用的同一块内存:
位置参数
调用函数时,Python语言必须将函数调用中的每个实参都关联到函数的相应形参。最简单的关联方式是基于实参的顺序,这种关联方式被称为位置实参。下面代码显示学生信息的函数,该函数输出学生的名字及年龄。
def describe_student(person_name, student_age):
"函数功能:显示学生的信息"
print("my name is ", person_name)
print(person_name + "is" + student_age + "years old")
describe_student('Jack', '24')
输出:
my name is Jack
Jackis24years old
上述函数describe_student()的定义表明,它需要姓名和年龄两个参数。调用describe_student()函数时,需要按顺序提供姓名和年龄参数。函数调用时,实参’Jack’ 存储在形参person_name中,而实参’24’ 存储在形参student_age 中。
定义了函数后,开发者可以根据需要多次调用函数。如果需要再描述一名学生,只需再次调用describe_student() 即可,如下代码所示。
def describe_student(person_name, student_age):
"函数功能:显示学生的信息"
print("my name is ", person_name)
print(person_name + "is" + student_age + "years old")
describe_student('Jack', '24')
describe_student('Bob', '17')
结果:
my name is Jack
Jackis24years old
my name is Bob
Bobis17years old
调用函数是一种效率极高
的开发方式。如在上例中,开发者只需在函数中编写描述学生的代码一次,以后需要描述新学生时,都可调用这个函数,并向它提供新的学生信息。即便描述全校的学生,依然只需使用一行调用函数的代码,就可实现所需功能。
在函数中,可根据需要使用任意数量的位置实参。Python语言将按顺序将函数调用中的实参关联到函数定义中相应的形参。但要注意的是,在使用位置实参来调用函数时,如果实参的顺序不正确,结果可能出乎意料,如下代码所示。
def describe_student(person_name, student_age):
"函数功能:显示学生的信息"
print("my name is ", person_name)
print(person_name + "is" + student_age + "years old")
describe_student('18', 'Jack')
结果:
my name is 18
18isJackyears old
在上述函数调用中,开发者先指定名字,再指定学生年龄。由于实参’18’ 在前,这个值将存储到形参person_name中;同理,‘Jack’ 将存储到形参student_age中。在实际开发中,如果执行结果和预期不一致,请核查函数调用中实参的顺序与函数定义中形参的顺序是否一致。
默认参数
编写函数时,可给每个形参指定默认值。在调用函数时,如果给形参提供了实参,Python语言将使用指定的实参值;否则,将使用形参的默认值。给形参指定默认值后,可在函数调用中省略相应的实参。使用默认值可简化函数调用,还可清楚地指出函数的典型用法。如下方式调用describe_student()函数会出现错误。
def describe_student(person_name, student_age):
"函数功能:显示学生的信息"
print("my name is ", person_name)
print(person_name + "is" + student_age + "years old")
describe_student('Jack')
提示错误信息:
Traceback (most recent call last):
File "D:/python_demo/demo_2.py", line 88, in <module>
describe_student('Jack')
TypeError: describe_student() missing 1 required positional argument: 'student_age'
上述代码中,提示的错误信息很明确,就是调用函数describe_student()时缺少了一个位置参数student_age。这个时候,默认参数就排上用场了
若大部分学生的年龄为18岁,开发者可以把第二个参数student_age的默认值设定为18,这样,当开发者调用describe_student(Jack)时,相当于调用describe_student(Jack,18) ,如下代码所示。
def describe_student(person_name, student_age='18'):
"函数功能:显示学生的信息"
print("my name is ", person_name)
print(person_name + "is" + student_age + "years old")
describe_student('Jack')
describe_student('Jack', '18')
结果:
my name is Jack
Jackis18years old
my name is Jack
Jackis18years old
对于年龄不是18岁的学生,就必须明确地传入student_age,如describe_student(‘Herbie’,19)。从上面的例子可以看出,默认参数可以简化函数的调用。
要注意的是,设置默认参数时,必选参数在前
,默认参数在后
,否则Python语言的解释器会报错。使用默认参数最大的好处是能降低调用函数的难度。编写一个学生注册的函数,需要传入name和gender两个参数。
def enroll(name, gender):
"注册学生的信息"
print("name:", name)
print("gender:", gender)
enroll('Jack', 'F')
结果:
name: Jack
gender: F
def enroll(name, gender, age='18', city='Beijing'):
"注册学生的信息"
print("name:", name)
print("gender:", gender)
print("age:", age)
print("city:", city)
enroll('Sarah', 'F')
print('-' * 70)
enroll('Sarah', 'M', '17')
结果:
name: Sarah
gender: F
age: 18
city: Beijing
----------------------------------------------------------------------
name: Sarah
gender: M
age: 17
city: Beijing
默认参数降低了函数调用的难度,而一旦需要更复杂的调用时,又可以传递更多的参数来实现。无论是简单调用还是复杂调用,函数只需要定义一个;当有多个默认参数时,调用时可以按顺序提供默认参数。
默认参数很有用,但使用时要牢记一点,默认参数必须指向不可变对象,否则会出现错误,如下代码所示。
def test_add(a=[]):
a.append('END')
return a
print(test_add([1, 2, 3]))
print(test_add(['a', 'b', 'c']))
print(test_add())
print(test_add())
print(test_add())
结果:
[1, 2, 3, 'END']
['a', 'b', 'c', 'END']
['END']
['END', 'END']
['END', 'END', 'END']
从上述代码可以看出,默认参数是空列表[],但是函数test_add()似乎每次都“记住了”上次添加了’END’后的list。这是因为在Python语言中,函数在定义的时候,默认参数H的值就被计算出来了,即[]。因为默认参数H也是一个变量,它指向对象[]。每次调用该函数,如果改变了H的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。
开发者也可以用None这个不可变对象来解决报错问题,如下代码所示。
def test_add(H=None):
if H is None:
H = []
H.append('END')
return H
print(test_add())
print(test_add())
结果:
['END']
['END']
对于str、None等类似的不可变对象一旦创建,其内部数据就不能修改,这样就减少了由于修改数据导致的错误。
此外,由于对象不变,多线程环境下同时读取对象不需要加锁,同时读也没有问题。开发者在编写程序时,如果可以设计一个不可变对象,就尽量设计成不可变对象。
不定长参数
在Python语言中,函数还可以定义不定长参数,也叫可变参数。给定一组数字a,b,c……,请计算a+b+c+ ……。要定义出这个函数,必须确定输入的参数。开发者可以把a,b,c……作为一个list或tuple传进来。
def calc(numbers):
sum = 0
for n in numbers:
sum = sum + n
return sum
print(calc([1, 2, 3])) # 结果是6
print(calc([1, 2, 3,4])) # 结果是10
对于以上定义的求和函数,调用的时候,需要先组装出一个list或tuple;在Python语言中,可以在函数参数前面添加“*”号把该参数定义为不定长参数;可以看出,不定长参数的使用使得calc()函数定义和调用都变得简洁,实例如下所示:
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n
return sum
print(calc(1, 2, 3, 4))
print(calc())
num = [1, 2, 3]
print(calc(*num))
结果:
10
0
6
关键字参数
关键字实参是传递参数时使用“名称–值”对的方式,在实参中将名称和值关联起来。
关键字实参让开发者无需考虑函数调用中的实参顺序,清楚地指出了函数调用中各个值的用途。
关键字参数有扩展函数的功能。
命名关键字参数
如果要限制关键字参数的名字,可以用命名关键字参数。和关键字参数**kw不同,如果没有可变参数,命名关键字参数就必须加一个“”号作为特殊分隔符。如果缺少“”,Python语言解释器将无法识别位置参数和命名关键字参数。例如,若只接收age和city作为关键字参数,可以采用如下形式。
def enroll(name, gender, *, age, city):
print(name, gender, age, city)
enroll('Jack', 'M', age='18', city='Beijing')
输出:
Jack M 18 Beijing
def enroll(name, gender, *grade, age, city):
print(name, gender, age, city)
enroll('Jack', 'M', '18', 'Beijing')
结果报错:
Traceback (most recent call last):
File "D:/python_spider/python_demo/demo_2.py", line 119, in <module>
enroll('Jack', 'M', '18', 'Beijing')
TypeError: enroll() missing 2 required keyword-only arguments: 'age' and 'city'
def enroll(name, gender, *, age='18', city):
print(name, gender, age, city)
enroll('Jack', 'M', city='Beijing') # 结果是:Jack M 18 Beijing
注意:
*表示不定长参数
**表示不定长的关键字参数
参数组合
在Python语言中定义函数,开发者可以组合使用这些参数(必选参数、默认参数、可变参数、关键字参数和命名关键字参数)。注意参数定义是有顺序的。定义的顺序
必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数
。比如要定义一个函数,包含上述若干种参数,如下代码所示。
def func(a, b, c=0, *args, **kw):
print('a=', a, 'b=', b, 'c=', c, 'args=', args, 'kw=', kw)
print(func(1, 2))
# 输出结果:a= 1 b= 2 c= 0 args= () kw= {}
print(func(1, 2, c=3))
# 输出结果:a= 1 b= 2 c= 3 args= () kw= {}
print(func(1, 2, 3, 'a', 'b'))
# 输出结果:a= 1 b= 2 c= 3 args= ('a', 'b') kw= {}
print(func(1, 2, 3, 'a', 'b', x=4))
# 输出结果:a= 1 b= 2 c= 3 args= ('a', 'b') kw= {'x': 4}
args = (1, 2, 3, 4)
kw = {'x': 5}
print(func(*args, **kw))
# 输出结果:a= 1 b= 2 c= 3 args= (4,) kw= {'x': 5}
函数式编程
高阶函数
接受函数为参数,或者把函数作为结果返回的函数称为高阶函数。例如,若要根据单词的长度排序,只需把len函数传给key参数。
fruits=['strawberry','fig','apple','cherry','raspberry','banana']
print(sorted(fruits,key=len))
# 输出结果如下:
# ['fig', 'apple', 'cherry', 'banana', 'raspberry', 'strawberry']
fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']
def reverse(word):
return word[::-1]
print(reverse('testing')) # 结果是:gnitset
print(sorted(fruits, key=reverse))
# 输出结果如下:
# ['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']
注意,上述例子中列表里的单词没有变,开发者只是把反向拼写当作排序条件,因此各种浆果(berry)都排在一起。在函数式编程范式中,最为人熟知的高阶函数有map、filter、reduce 和apply。其中,apply函数在Python2.3中标记为过时,在Python3已移除。
匿名函数
所谓匿名函数,即不再使用def语句
这样标准形式定义的函数。Python语言经常使用lambda
来创建匿名函数。lambda 只是一个表达式,函数体比def定义的函数体要简捷。lambda函数的语法如下所示。
lambda [arg1[,arg2],....argn]]:expression
举例:
sum = lambda arg1, arg2: arg1 + arg2
print(sum(1, 2)) # 结果是:3
上述代码中,第一行定义了一个lambda函数,执行两个数的和运算,并且把该lambda函数命名为sum。会面的代码通过sum()函数即实现了调用lambda函数的功能。
类
面向对象编程概述
根据代码组织方式的不同,编程语言可以分为以下两种。
面向过程语言
:面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再依次调用,类似流水线的工作原理。面向对象语言
:面向对象是把构成问题事务分解成各个对象,依靠各个对象之间的交互推动程序执行,进而实现问题的解决。建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在完整解决问题步骤中的行为。OOP的产生
面向对象编程的由来如下所述:
使用传统的面向过程编程机制构造系统时,在重用、维护、扩展等方面会出现诸多问题,且逻辑过于复杂,代码易读性差。人们开始思考能不能模拟现实环境,以人类解决问题的方法、思路、习惯和步骤来设计相应的应用程序。面向对象的编程思想(Object-Oriented Programming
,OOP
)就产生了。
面向对象其产生的原因主要有以下两点:
- 面向过程的编程机制无法解决复杂程序的可维护性和可扩展性问题。
- 面向过程的编程机制背离了人们观察和解决问题的基本思路。
简言之,面向过程的编程是根据业务逻辑
从上到下
写代码,典型如C语言
等;
面向对象的编程是对变量和函数
进行分类和封装
,让开发“更快更好更强”,典型如C++
、Java
、C#
等。对这两种编程机制,Python
均提供了良好支持。
总体而言,Python
更偏向于成为面向对象编程
的编程语言。
OOP核心思想
面向对象编程的核心思想是把构成问题的各个事物分解成能够完整描述该实体功能的封装类,并通过对象的交互完成问题的解决。
对象
作为程序的基本单位
,将程序和数据封装于其中,以提高程序的重用性、灵活性和可扩展性。
类
是创建对象
的模板
,而对象
是类的实例化
。一个类可以创建多个对象。
类 :是对实体的抽象,是泛指,比如:动物、植物等。
对象:是类的一个实例,是特例,比如:猫、狗等。
例如:动物可以对猫的特征和行为进行抽象,然后可以实例化为真实的动物实体。
在采用面向对象思想编程时,可依次采用以下步骤:
(1)分析哪些动作是由哪些实体发出的;
(2)定义这些实体,为其增加相应的属性和功能;
(3)让实体去执行相应的功能或动作。
OOP特征
面向对象的编程机制有以下三大特征:
封装:找到变化并且把它封装起来,就可以在不影响其它部分的情况下修改或扩展被封装的变化部分,这是所有设计模式的基础。封装解决了程序的可扩展性。
继承:子类继承父类,可以继承父类的方法及属性,实现了多态以及代码的重用,解决了系统的重用性和扩展性。
多态:接口的多种不同的实现方式即为多态。接口的主要目的是为不相关的类提供通用处理服务。这实现了系统的可维护性和可扩展性。
类和对象
类的创建
Python语言中,使用class关键字来创建类,其创建方式如下:
class ClassName(bases):
# class documentation string 类文档字符串,对类进行解释说明
class_suite
class是关键字
,bases是要继承的父类
,默认继承object
类。
class documentation string是类文档字符串
,一般用于类的注释说明
。class_suite是类体
,主要包含属性
和方法
。
类、属性和方法的命名约定惯例如下:
类名
表示实例的抽象,命名时首字母大写
;属性
使用名词作为名字,比如name、age、weight等;方法
名一般指对属性的操作,其命名规则一般采用动词加属性名称形式,如updataName、updataAge、updataWeight等。 举例如下图:# 类定义
class People: # 类名
name = "张三" # 属性名
def undate_name(self, name):# 方法名
self.name = name
Python的类分为以下两种:经典类
:Python2.x中类定义的默认方式,不继承object类,其内部由属性和方法组成。经典类定义的典型方式如下图:
# 经典类是指没有继承object类
class A:
pass
新式类
:Python3.x中类定义的默认方式,必须继承object方法,其典型定义方式如下图:
# 新式类是指继承object的类
class A(object):
pass
新式类
修复了经典类的一些bug(如多重继承时的歧义等),并且提供了对类方法
和静态方法
的支持。
在Python3.x中,如果没有显示指明要继承的父类,则默认继承object
类。
class A:
pass
class A():
pass
class A(object):
pass
Python3.x中,无需继承时,例子中三种类的创建效果一样。自动继承object类。
新式类添加了一些内置属性和方法,如下所示:
__name__ :属性的名字
__doc__ :属性的文档字符串
__get__(object) :获取对象属性值的方法
__set__(object, value) :设置对象属性值的方法
__delete__(object, value) :删除对象属性的方法
对象的创建
类创建完之后,就应该创建该类的实例或对象了,该过程称之为实例化。当一个对象被创建后,就包含标识、属性和方法这三个方面的对象特性了。其中,对象标识用于区分不同的对象,属性和方法与类中的成员变量和成员函数相对应
people = People("李四", 20, "50kg") # 实例化一个对象
如例子所示,对象标识符为people,属性为括号中内容,方法为类中方法
类的属性
Python语言中,属性分为类级别和实例级别两种。实例级别的属性值默认共享类级别的属性值。除非显式进行赋值操作。下面举一个例子来说明。
class A():
age = 10
obj2 = A()
obj3 = A()
如例子所示,存在三个实例,分别是类实例
A和对象实例
obj2、obj3。
在情形1中,obj2和obj3这两个对象实例共享类实例A的属性age;
# 情形1
print(obj2.age, obj3.age, A.age)
在情形2中,显示修改了对象实例obj1的属性aaa;
# 情形2
obj2.age+=2
print(obj2.age, obj3.age, A.age)
在情形3中,修改了类实例A的属性aaa。
# 情形3
A.age+=3
print(obj2.age, obj3.age, A.age)
结果如图所示:
情景1:
10 10 10
情景2:
12 10 10
情景3:
12 13 13
在情形2中已经修改了对象实例obj2的属性值age,其属性值和类实例A的属性值已经独立。而对象实例obj3的属性从来没有修改过,所以它还是和类实例A的属性值保持一致。
Python语言对于属性的设置采用“类.属性 = 值
”或“实例.属性 = 值
”的形式。如上例中obj2.age += 2等价于
obj2.age = obj2.age + 2,该语句包含了属性获取及属性设置两个操作。
Python语言中的属性操作遵循三个规则:
(1)属性的获取是按照从下到上的顺序来查找属性;
(2)类和实例是两个完全独立的对象;
(3)属性设置是针对实例本身进行的。
类的定义由属性和方法组成,属性
是对数据
的封装,方法
则是对类行为
的封装。属性按使用范围分为公有属性
和私有属性
,使用范围取决于属性名称。类的属性如下表所示。
内置属性如下表所示:
类的方法
类方法也包括公有方法、私有方法、类方法和静态方法。如下表介绍:
类的方法举例如图所示:
类方法和静态方法原理上有以下区别:
(1)静态方法不能使用self的方式调用。
(2)静态方法调用时会预先将类中用到的属性和方法进行加载,而类方法则是随调随用。因此,类方法相比静态方法具有不占资源的优势,但是速度不及静态方法。
(3)静态方法调用类中的属性时需要使用“类名.属性”的格式。
内部类
内部类:类的内部定义的类,主要目的是为了更好抽象现实世界。
一般情况下不使用内部类,这样会使程序结构复杂,但是理解内部类有助于理解模块的调用。
下面例子中,People类中又定义了Father类和Mother类两个内部类。创建内部类的实例化对象,可以通过外部类的实例化对象调用内部类完成,如Lisi = Zhangsan.Father();也可以直接使用外部类名调用内部类,如Liming = People.Mother()。
class People():
code = 0
class Father():
code = 1
class Mother():
code = 2
zhangsan = People()
lisi = zhangsan.Father() # 第一种实例化方法
print(lisi.code) # 输出结果:1
liming = People.Mother() # 第二种实例化方法
print(liming.code) # 输出结果:2
从例子可以看出,内部类调用有两种方式。
(1)直接使用外部类调用内部类;
(2)先对外部类进行实例化,然后再实例化内部类。
魔术方法
在Python语言中,所有以双下划线“__”包起来的方法,都统称为“魔术方法”。
这些方法在实例化时会自动调用,
比如“_str__()”、“__init__()”、“__del__()”等。
使用魔术方法可以构造出非常优美的代码,比如将复杂的逻辑封装成简单的API等。
魔术方法中的“__init__()”方法一般叫做构造函数,用于初始化类的内部状态和参数。
如果不提供,Python语言会给出一个默认的“__init__()”方法
魔术方法中的“__ del __()”函数叫做析构函数,用于释放对象占用的资源。“__del__()”函数是可选的,
如果不提供,Python语言会在后台提供默认析构函数。
魔术方法中,有些可以实现属性访问控制的功能,如“__getattr__(self,name)”,
“__setattr__(self,name,value)”方法等。
魔术方法举例:
class People():
name = "人"
def __init__(self, n="非洲人"):
self.name = n
print("我是构造函数", self.name) # 重写构造函数
def __del__(self):
print("我是析构函数", self.name) # 重写析构函数
zhangsan = People()
lisi = People("欧美人")
zhangsan.__del__() # 调用析构函数
print(zhangsan)
del zhangsan
# print(zhangsan) 出现错误,因为del后,对象就不存在了
结果如下:
我是构造函数 非洲人
我是构造函数 欧美人
我是析构函数 非洲人
<__main__.People object at 0x000001EAF4D09358>
我是析构函数 非洲人
我是析构函数 欧美人
如例子所示,对于这些魔术方法,在创建对象时可以自动执行。当对象自动执行析构函数“A.del()”后,对象仍然存在,但是在调用“del A”,后,对象就已经被回收删除,无法再次使用。
类间关系
依赖关系
用实例方法执行某个功能时,如果需要使用另一个类的实例的方法来完成,则称这两个类之间存在关联关系。下面是个例子
class Person():
def play(self, tools):
tools.run()
print("很开心,能玩游戏了")
class Computer():
def run(self):
print("电脑开机,可以运行")
class Phone():
def run(self):
print("手机开机,可以运行")
c = Computer()
phone = Phone()
p = Person()
p.play(phone)
结果:
手机开机,可以运行
很开心,能玩游戏了
例子定义了Person、Computer、Phone三个类,在Person类的play()方法中,可以使用Computer类或Phone类的实例作为参数。因为在Person类方法中实现时需要Phone类实例调用方法完成,所以这两个类之间存在依赖关系。
关联关系
一个类的属性类型是另外一个类的类型,则称这两个类之间存在关联关系。根据属性是单值或多值,关联关系又分为一对一关联、一对多关联等。一对一关联举例如下。
例子中表示的是一对一关联,Boy类中的属性girlFriend是Girl类的实例,这两个类之间存在一对一关联关系。
一对多关联:
例子中表示的是一对多关联,School类中的属性teach_list是Teacher类的实例集合,这两个类之间存在一对多关联关系。
继承关系
类继承是在已有类基础上构建新类的机制,该新建类也成为子类。子类可以增加新的属性或功能,也可以继承父类的功能。继承所描述的是“is-a”的关系。
通过继承机制,可以复用以前代码,大大提高开发效率。
(感谢博主:跳楼梯企鹅 提供的图片和修改建议)
如图所示,猫和狗是继承者称之为子类
或者派生类
。动物是被继承者称之为父类
或者超类
。
而波斯猫和巴厘猫又继承自猫类,对波斯猫类来说,它的父类就是猫类。
例子中,首先定义了一个People类,接着从该类派生出两个子类Japan和China,然后同时以这两个类为父类,派生出类Ren。一般来说,为防止出现歧义,尽量在类定义时候避免多继承。
在Python语言中使用继承机制时,有以下几点需要注意:
(1)子类拥有父类的属性和方法。
(2)子类可以创建自己属性和方法。
(3)子类可以对父类的方法进行覆盖实现。
(4)子类可重新定义父类中的属性。
(5)一个父类可由多个子类继承,一个子类也可继承多个父类。
(6)如果父类定义了__init__()方法,子类也定义了自己的__init__()方法并且还要使用父类的__init__()方法,
子类需要显式调用父类的__init__()方法。如果子类需要扩展父类的初始化行为,可以添加__init__()方法参数。
(7)当继承的多个父类中有相同的属性或方法时,会使用最后一个继承父类的属性或方法。
继承机制的出现,导致父类和子类相同行为出现的可能,以及在实际运行中的动态结果,此为多态。
多态(是从定义角度出发):同一类事物的多种形态,如猫的多种形态:白猫、黑猫、花猫等。
多态性(是从使用角度出发):同一种调用方式,不同的执行效果。具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。
多态性依赖于继承机制,并且要定义统一的接口。
在程序语言中,多态实质就是定义了一个函数接口,在这个函数中定义了所有类中通用的功能。根据不同对象的相同调用接口,就得到不同的结果。下面例子是多态的实现。
class Animal():
def run(self):
raise AttributeError("子类必须实现这个方法")
class Cat(Animal):
def run(self):
print("猫在跑")
class Pig(Animal):
def run(self):
print("猪在跑")
class Dog(Animal):
def run(self):
print("狗在跑")
a=Cat()
b=Pig()
c=Dog()
a.run()
b.run()
c.run()
输出结果:
猫在跑
猪在跑
狗在跑
这种写法最大的问题就是接口不统一,有没有一种统一接口的写法呢?如下所示:
def func(n):
n.run()
a = Cat()
b = Pig()
c = Dog()
func(a)
func(b)
func(c)
例子中演示了多态性定义和调用的两种方式。也体现了多态依赖的两个步骤。首先,多态来自同一个父类的继承;其次,为这些不同的子类定义相同的调用接口。
使用多态性有如下优点:
(1)以不变应万变,增加了程序的灵活性。不论对象千变万化,调用方式都相同。
(2)增加了程序的可扩展性。对于子类来说,使用者无需更改调用代码。
顺序语句结构
程序有三种基本的控制结构:顺序结构
、选择结构
、循环结构
。
顺序结构
顺序结构
是最简单也是最常见的一种结构,采用顺序结构的程序通常按照由前到后的顺序执行各个语句
,直到程序结束,结构如下所示:
顺序结构:
顺序结构实例
举例如下:
a = 2
b = 3
c = a + b
d = c * 24
print(a, b, c, d)
条件与分支
条件语句是通过判断条件是否成立,根据条件表达式的结果做出决策,控制不同代码块的执行。
条件表达式
条件表达式由运算符
和操作数
组成
例如:a<4,其中a、4都是操作数,小于符号<是运算符
判断条件可以是具有布尔属性的任意元素,包括数据、变量或由变量与运算符组成的表达式,若其布尔属性为True,条件成立;若布尔属性为False,条件不成立。
除了非空常量外,Python还常使用关系操作符和成员运算符构成判断条件 。
条件表达式常用的运算符有:
算术运算符:+、-、*、/、//、%、**
关系运算符:
Python支持通过保留字not、and和or对判断条件进行逻辑组合:
单分支选择结构
示例:使用单分支结构判断当天是否是星期天。
day = int(input("今天是工作日吗(请输入整数1~7)?"))
if day in [1,2,3,4,5]:
print("今天是工作日。")
if day in [6,7]:
print("今天非工作日。")
双分支选择结构
示例:使用二分支结构判断当天是否是工作日。
day = int(input("今天是工作日吗(请输入整数1~7)?"))
if day in [1,2,3,4,5]:
print("今天是工作日。")
else:
print("今天非工作日。")
多分支选择结构
选择结构的嵌套
选择结构的嵌套是指选择结构的内部包含选择结构
三元表达式
python中没有c语言中的三目运算符,但是可以通过以下的形式实现三目运算符的功能
格式:条件判断为真时的结果 if 判断条件 else 条件为假时的结果
示例:x=x-1 if x>0 else x=x+1
等价于:
if x > 0:
x = x - 1
else:
x = x + 1
我们可以利用三元表达式来实现裴波那契数列:
def fun(n):
return n if n < 2 else fun(n - 1) + fun(n - 2)
还有一种用法,用bool方法选择相应的值,例如:
x = 1
print([2, 3][bool(x)])
x = 0
print([2, 3][bool(x)])
结果:
3
2
循环
在实际应用中有些需要重复进行的操作,可以用循环语句实现。
循环语句有:
while、do while、for
while循环
例如:使用while循环实现计算n的阶乘
n = int(input("请输入一个整数:"))
fact = 1
i = 1
while i<= n:
fact = fact*i
i = i + 1
print("n!={}".format(fact))
while else循环
Python的while循环也支持使用保留字else产生分支。
示例2:使用while-else循环实现计算n的阶乘
n = int(input("请输入一个整数:"))
fact = 1
i = 1
print("n!计算中……")
while i<= n:
fact = fact*i
i = i + 1
else:
print("n!计算完成 ,循环正常结束")
print("n!={}".format(fact))
for 循环
示例1:遍历字符串中的每个字符
string = input("请输入一个字符串:")
for c in string:
print(c)
for else循环
for…else循环的具体实现形式:
for 循环变量 in 目标:
循环体
else:
代码块
用法与while…else相同,如果循环体结束后没有碰到break语句,就会执行else语句块,如果结束之前碰到了break语句,就会跳出整个for循环,因此else语句块也就不会执行。
循环控制语句
·在循环语句中,有时候需要达到中断循环,或者跳过本次循环,执行下次循环的情况,因此就需要有循环控制语句
python中使用break、continue语句控制循环的执行过程
break用于跳出整个循环
continue用于跳出本次循环,继续执行下次循环
break语句
continue语句
pass语句
pass的意思是过,pass掉就是淘汰掉的意思。
在python中,pass的意思是空语句,pass语句不做任何事情,只是为了保持程序结构的完整性。
数据类型
python的数据类型分为基本数据类型
和复合数据类型
基本数据类型
包含:数字
,字符串
复合数据类型
包含:列表
,元组
,字典
,集合
数字类型
和其他编程语言相比,Python语言中的变量不需要显示数据类型的声明,Python的变量类型是根据变量的赋值内容而自动生成的。
Python 六大数据类型:
- 数字: 包含int(整型)、
long
(长整型,但python3中已经没有了
,python3中只有int)、complex(复数)、float(浮点型)、bool(布尔型) - 字符串(String): 比如:“hello world”,“python”
- 列表(List): 比如:[1,2,3,4],[’a‘,‘b’,‘c’,‘d’]
- 字典(Dictionary):例如:{“name”:“poge”,“age”:30}
- 元组(Tuple): 例如:(1,‘hello’,3)
- 集合(Set):例如:{’a‘,’b‘,’c‘,’d‘}
以上是Python最基本的6
种数据类型
数字类型:
表示数字
或数值
的数据类型称为数字类型
。
数字类型有哪些?
- 整型(int)
- 浮点型(float)
- 复数类型(complex)
- 布尔类型(bool)
tips:Python中使用type()
函数可以查看变量的类型
整数
Python3中整型已经不分 int和long了,只有int型!
整型表示形式:
- 二进制:以“0B”或“0b”开头(比如0b101)
- 八进制:以 “0o”或“0O”开头(比如0o510)
- 十进制:比如10、29、40
- 十六进制 :以“0x”或“0X”开头(比如0xA7A)
整型不同形式的转换
- bin():十进制转换为二进制,输出形式是字符串
- oct():十进制转换为八进制,输出形式是字符串
- hex():十进制转换为十六进制,输出形式是字符串
- int() :接收一个符合整型规范的字符串,并将字符串转换为整型
tips1:Python中当多个变量的值相同时,这个值在内存中并没有被保存多次,只是多个变量都指向了同一内存,举例如下:
a = 6
b = 6
c = a
print(id(a), id(b), id(c))
输出:
1643146464 1643146464 1643146464
id()可以取出变量的内存地址,从结果可以看出,a,b,c三个变量都指向了同一内存。但如果修改了a的值,那a的内存地址和b,c的内存地址及值有什么变化呢?举例如下:
a = 6
b = 6
c = a
print(id(a), id(b), id(c))
a = 8
print(id(a), id(b), id(c))
结果:
1643146464 1643146464 1643146464
1643146528 1643146464 1643146464
从上面可以看出,a的地址变了,b,c的地址没有变。
数字时Python中的不可变对象,当改变它的值时,并不是真的修改内存中变量的值,而是把新的值放入内存中,然后修改变量,使得变量指向新值的内存地址。浮点数、复数等其他数字类型及其他类型的变量具有同样的特点。
tips: Python具有自动内存管理功能,如果一个值没有任何变量指向,则Python自动将其删除,因此,一般Python程序员不需要太多考虑内存管理的问题(这就是Python与C语言最大的区别,自动内存回收机制!),但显式使用del命令删除不再使用的值,仍然是一个优秀程序员需要具备的基本素养!
浮点数
所谓浮点数
就是数学中的小数
整数
和小数
部分(可以是0)组成。科学计数法就是用字母e或E作为幂的符号,以10为基础,格式:xey表示的就是x乘以10的y次幂,例如:
1.2e-2:1.2乘以10的-2次幂 = 0.012
2e3:2乘以10的3次幂 = 2000
例如:
a = 1.2
print(type(a))
b = 1e-2
print(type(b))
c = 2e3
print(c)
输出:
<class 'float'>
<class 'float'>
2000.0
复数
Python中表示复数的两种方法:
- a+bj
- complex(a,b)
其中a表示实部,b表示虚部
举例:
a = 2 + 6j
print(type(a))
b = complex(2, 6)
print(type(b))
print(id(a), id(b))
结果:
<class 'complex'>
<class 'complex'>
2718471372752 2718437554352
获取复数的实部、虚部、共轭复数等
a = 2 + 6j
print(a.imag) # .imag可以获取复数的虚部
print(a.real) # .real可以获取复数的虚部
print(a.conjugate()) # .conjugate()方法可以获取复数的共轭复数
结果:
6.0
2.0
(2-6j)
布尔类型
等同于False的值:
等同于True的值:
and
和or
运算有一条重要的法则:短路法则
and举例:
a and b ,如果a是False,则整个结果必定为False,因此返回a的值,如果a是True,则表达式的结果取决于b,因此返回b
a = 0
b = 2
print(a and b)
a = 1
b = 0
print(a and b)
a = 1
b = 3
print(a and b)
结果:
0
0
3
or举例:
a or b ,如果a是True,则整个结果必定为True,因此返回a的值,如果a是False,则表达式的结果取决于b,因此返回b
a = 0
b = 2
print(a or b)
a = 1
b = 0
print(a or b)
a = True
print(a and 'z=T' or 'z=F')
2
1
z=T
数值运算
常见的数值运算有:加、减、乘、除、取余、指数运算等
例如:a=2 ,b=8
例如:
a=3
b=6
a+b #加法
Out[4]: 9
a-b #减法
Out[5]: -3
a*b #乘法
Out[6]: 18
a**2 # 指数运算 a的2次方
Out[7]: 9
a%b #取余
Out[8]: 3
Python有两种除法:/
和 //
/
:真除法//
:整除法,向下取整
例如:
a = 6
b = 4
print(a/b)
print(a//b)
b = -4
print(a/b)
print(a//b)
结果:
1.5
1
-1.5
-2
数值计算函数库
Python内置了很多实用的数值函数,例如:
另外,还有随机数函数用途也是有很多的:random模块
import random
print(random.random()) # random.random()作用是生成一个[0-1]范围内的随机数
print(random.randint(1, 2000)) # random.randint(a,b)作用是生成一个[a-b]范围内的随机整数
0.48771584654043476
58
type函数
type的用途是,当你不知道变量的数据类型时,可通过它来查询,type时内置函数,可以直接使用,不需要导包。
a = 2
b = -1.1
c = "abc"
d = ['a', 'b']
print(type(a))
print(type(b))
print(type(c))
print(type(d))
<class 'int'>
<class 'float'>
<class 'str'>
<class 'list'>
字符串类型
2.1 字符串表示
在python语言中,字符串的表示有以下三种:
单引号、双引号
在Python语言中,使用单引号
(’ ')和双引号
(" ")表示字符串是最常见的两种方式。
两种方式表示的字符串是等价
的,且返回相同类型的对象,如下代码所示:
单引号和双引号表示字符串没有本质的区别,可以
在一种引号中嵌套
另一种引号。
单引号和双引号表示的字符串是等价的,要配对出现
,不能混用。否则将引发语法错误。
三重引号
三重引号可以是三个单引号
,也可以是三个双引号
。这种方式表示的字符串也叫做块字符串
。
三重引号是以三个同一种类型的引号开始,并以三个相同引号结束的字符串表示方式。
从上述代码中可以看出:
字符串变量spring的内容包含三行,以三个双引号开始和结束。从输出结果可以看到,字符串中出现了“\n”换行符。这是因为在每行结束时,都使用了回车并且换行到下一行
转义字符
Python语言使用反斜杠()表示转义字符。
转义字符是为了对其后紧接的字符进行转义,屏蔽其特殊含义,并将其作为普通字符来输出。
从代码中可以看出,需要在句子中显示一个特殊字符“ ’ ”,要用到转义字符\
,才能正确输出。
下表中列出了一些常用的转义字符及其含义:
raw字符串
raw字符串的格式是r’…’。
在raw字符串中,所有的字符都是直接按照字面意思来解释,没有转义字符或者不能打印的字符。
看下面的例子:
字符串操作
Python语言中包含字符串的以下几个基本操作:
索引和分片
-
索引
在Python语言中,字符串是一个有序字符的集合
。
在一个字符串被创建之后,其中字符的相对位置就固定了。第一个字符的索引编号定义为0,第二个字符索引编号为1,以此类推。在Python语言中,对字符串可以进行正向索引和反向索引。
下表直观表示了字符串中字符和其索引编号的对应关系:
上表中,中间行表示的是字符串中的每个字符内容,上面行表示该字符串正向索引时的索引编号,下面行表示该字符串反向索引时的索引编号。索引是对单个元素进行的操作,Python语言中字符串的索引是通过元素下标进行索引的。
上述代码中,当通过下标5索引元素时,出现了下标越界的错误。因为字符串s的下标最大是4(从0开始)。 -
切片
使用Python语言的分片(slice)操作,来提取字符串中的子序列。
代码中:s[1:3]获取s中下标从1到3的子字符串;s[1:] 获取从下标为1到字符串末尾的元素;s[:-1]获取从开始开始到字符串末尾的元素。s[::2]下标从0开始,每隔一个字符取一个元素;s[::-1]实现对字符串的逆序操作。
连接字符串
字符串的连接就是把两个或多个字符串连接成一个字符串。
在连接字符串时,Python语言会为每个连接的字符串及新产生的字符串分配内存,增大不必要的内存开销。
- 操作符(“+”) 方法:
- Python语言使用符串格式化操作符(%)和join()方法这两种方式连接字符串。
操作符(%) 方法:
join()方法:
修改字符串
在Python语言中,字符串属于不可变类型,不能
修改或删除原字符串中的字符。
通过加号的连接方式:
通过对原字符串切片再连接方式:
replace()函数来修改字符串:
replace()函数的功能是字符替换,如果要替换的源字符或字符串存在,且和待替换的目标字符或字符串不同,则返回值为重新创建的一个字符串对象;否则,返回原字符串对象。
代码中用replace()函数把字符串s中的’p’替换为’C’。
其他操作
通过帮助函数可以查看字符串对象的操作方法。这些方法可以通过object.attribute的方式调用。
上述列表中的方法都是可以直接调用的字符串操作方法,其中包含了上节提到的replace()方法。
字符串格式化
字符串格式化是对字符串的输出形式进行控制,使其按照开发者期望的方式输出。
进行字符串格式化的方法主要分为三种:
符号格式化
符号格式化主要是使用“%+格式化符号”,及相应的格式化辅助符号的方式对字符串进行格式化。
常用的字符串格式化符号如下表所示:
举例:
print("%c,%c" % (65, 97))
s = 'podjsd'
print("%s" % (s))
print('%r'%42)
print('%e'%200.21)
print('%g'%200.21)
print('%%'%())
结果:
A,a
podjsd
42
2.002100e+02
200.21
%
常用格式化辅助符号如表5-4所示:
print('%5.2f'%(3.4))
print('%+d'%(4))
结果:
3.40
+4
函数格式化
format()方法对字符串进行格式化操作,format()方法常用的匹配方法有三种:
例如:
print("a={},b={}".format(2, 4))
print("a={0},b={1}".format(2, 4))
print("a={1},b={0}".format(2, 4))
print("a={num2},b={num3}".format(num2=2, num3=4))
print("a={num3},b={num2}".format(num2=2, num3=4))
结果:
a=2,b=4
a=2,b=4
a=4,b=2
a=2,b=4
a=4,b=2
以下表格对常用的格式化输出形式予以介绍:
字典格式化
在Python语言中,字典格式化是在左边的格式化字符串通过引用右边字典中的键来提取对应的值,实现格式化输出。
print("%(name)s,%(age)s" % {"name": "hupo", "age": 18})
结果:
hupo,18
需要注意的是括号外的s别忘了:“%(name)s,%(age)s”
复合数据类型
List(列表)
列表的创建
创建列表:变量名 = [元素1,元素2,…,元素n]
list类型中区分元素的顺序,且允许包含重复的元素。
基本操作
1. 索引
列表每一个元素都对应一个整数型的索引值,可以通过索引值来得到相应的元素值。同时支持列表元素的正向索引和反向索引。
正向索引即索引值为正,从0开始;
反向索引即索引值为负,从-1开始。若是反向索引,则-1为末尾元素对应的索引编号。
2. 切片
切片操作可以截取列表变量中的部分元素,并返回一个子列表变量。
切片操作中,生成子列表的元素包含起始索引对应的元素,但是不包含终止索引对应的元素。
3. 加法和乘法
4. 修改和删除
5. 追加插入和扩展
多维列表
创建三个列表类型的变量a、n和x。其中,变量a和n中元素都是基本类型,变量x中的元素都是列表类型。
>>> a = ['a',1]
>>> n = ['b',2]
>>> x = [a,n]
>>> x
[['a',1],['b',2]]
>>> x[0] # 显示第一个元素
['a', 1]
>>> x[0][1] # 显示第一个元素中的第二个元素
1
直接输入列表的数据,系统会根据输入生成相应的列表维度。
>>>l = [[1,2,3],[4,5,6]]
>>>print(l)
输出结果:
[
[1,2,3],
[4,5,6]
]
也可以使用列表解析方式生成了二维矩阵
cols = 7
rows = 9
list_2d = [[col + 1 for col in range(cols)] for row in range(rows)]
print(list_2d)
迭代器
首先创建了一个列表类型变量lst,然后创建了该列表的迭代器对象lstiter,并且通过该迭代器对象的__next__()方法遍历列表中的元素。__next__()
方法,返回下一个值。iter方法访问列表。
列表解析
>>>list=[1,2,3,4,5,6,7,8,9,10] #方法1:直接指定
>>>list=[]#方法2:先创建一个空列表,然后通过for循环实现
for n in range(1,11):
list.append(n);
>>>list(range(1,11)) #方法3:列表解析
[1,2,3,4,5,6,7,8,9,10]
for循环在这里可以很好的生成list
首先针对数值运算,使用列表解析生成了1到10的平方值;
其次,针对字符运算,使用列表解析生成了指定范围的字母组合列表。
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 或者字母所进行的两层循环
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
列表函数和方法
tuple(元组)
元组的创建
创建元组:变量名 = (元素1,元素2,…,元素n)
注意:
当元组中只包含一个
元素时,需要在元素后面添加逗号
,否则括号会被当作运算符使用。
基本操作
1. 访问
使用下标索引来访问相应的元素值。也可采用正向索引和反向索引两种方式,并支持切片操作。
2. 修改
元组中的元素值是不允许修改的,可以对元组进行连接生成新的元组。
3. 删除
元素值不允许删除的,但可使用del语句删除整个元组。
需要注意的是,删除后的元组对象不可再次引用。
4. 统计
通过内置的count方法统计某个元素出现的次数。
5. 查找
通过内置的index方法查找某个元素首次出现的索引位置。
元组函数和方法
表中的count方法和index方法,实际是元组类型和列表类型所共有的方法,其使用方法和列表完全相同。
元组的优势
dict(字典)
字典的创建
创建字典对象:变量名=(key1:value1, key2:value2,…, keyn:valuen)
3.3.2 基本操作
1. 访问
字典是无序的,没有索引,不能通过下标索引。通过对key值的索引进行访问。
2. 修改
通过对key值的引用对value值的修改操作。
3. 删除
字典的删除操作中,可以删除某个元素或删除整个字典,也可以清空字典元素。.
字典的嵌套
>>>Va1 = {a:{b:1,c:2},d:{e:3,f:4}} #字典的value值是字典
>>>Va2 = {a:[1,2,3],b:[4,5,6]} #字典的value值是序列
>>>n1={'surname':'wang','name':'gang'}
>>>n2={'surname':'zhang','name':'san'}
>>>n3={'surname':'liu','name':'wen'}
>>>n4=[n1,n2,n3] #序列的元素是字典
n1、n2、n3是字典类型的变量,n4是列表类型变量,且n4中的元素即为n1、n2、n3。
3.3.4 字典的遍历
username={'full_name':'ZhangWei', 'surname':'Zhang', 'name':'Wei' }
#遍历所有的键-值对
for k,v in username.items():
print('key:'+k)
print('value:'+v+'\n')
#遍历所有键
for k in username.keys():
print(k)
print('key:'+k+'-value:'+username[k])
#遍历所有的值
for v in username.values():
print(v)
字典函数和方法
set(集合)
集合的创建
创建集合对象:变量名 = {元素1,元素2,…,元素n}
集合特性:
注意:
创建空集合用set();
{}创建一个空字典。
集合的数学运算
数学运算后依然保持集合的特性,没有重复的元素
基本操作
1. 更改
2. 删除
不可变集合
集合函数和方法
内置类
数据类型 | 表示方法 | 特性 |
---|---|---|
list | 列表用方括号表示:[] | list是一种有序的集合,可以随时添加和删除其中的元素。和C++数组的区别就是类型可不同。 |
tuple | 元组用圆括号表示:() | 和list相比唯一的差异在于元组是只读的,不能修改。 |
dict | 字典用花括号表示:{} | 列表是有序的对象结合,字典是无序的对象集合。两者之间的区别在于:字典当中的元素是通过键来存取的,而不是通过偏移存取。 |
set | set() | 集合是一个无序不重复元素集,基本功能包括关系测试和消除重复元素 |
列表list
初始化列表
指定元素初始化列表
>>> num=['aa','bb','cc',1,2,3]
>>> print num
['aa', 'bb', 'cc', 1, 2, 3]
从字符串初始化列表
>>> a='oiawoidhoawd97192048f'
>>> num=list(a)
>>> print num
['o', 'i', 'a', 'w', 'o', 'i', 'd', 'h', 'o', 'a', 'w', 'd', '9', '7', '1', '9', '2', '0', '4', '8', 'f']
从元组初始化列表
>>> a=(1,2,3,4,5,6,7,8)
>>> num=list(a)
>>> print num
创建一个空列表
>>> num=[]
>>> print num
[]
用某个固定值初始化列表
>>> initial_value=0
>>> list_length=5
>>> sample_list=[initial_value]*list_length
>>> print sample_list
[0, 0, 0, 0, 0]
>>> sample_list=[initial_value for i in range(10)]
>>> print sample_list
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
访问列表
访问单个元素
>>> num=[0,1,2,3,4,5,6,7]
>>> num[3]
3
>>> num=[0,1,2,3,4,5,6,7]
>>> num[0]
0
>>> num[-1]
7
>>> num[-3]
5
遍历整个列表
num=[0,1,2,3,4,5,6,7]
for a in num:
print a,
for i in range(len(num)):
print num[i],
输出结果:0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
列表操作
更新列表
>>> num=[0,1,2,3,4,5,6,7]
>>> num[1]='abc'
>>> print num
[0, 'abc', 2, 3, 4, 5, 6, 7]
删除列表元素
num=[0,1,2,3,4,5,6,7]
for i in range(len(num)):
print num[i],
del num[2]
print num
输出结果:0 1 2 3 4 5 6 7 [0, 1, 3, 4, 5, 6, 7]
列表操作符+*
列表对+和的操作符与字符串相似。+号用于组合列表,号用于重复列表。
以下为+操作符
>>> a=['a','b','c']
>>> b=['d','e','f']
>>> c=a+b
>>> print c
['a', 'b', 'c', 'd', 'e', 'f']
以下为*操作符
>>> a=['a','b','c']
>>> c=a*4
>>> print c
['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c']
列表函数
以下是列表相关函数的分类
xmind文件可点这里下载
以下是help(list)
的结果中关于重点函数的介绍部分
Help on list object:
class list(object)
| list() -> new empty list
| list(iterable) -> new list initialized from iterable's items
|
| Methods defined here:
|
| append(...)
| L.append(object) -> None -- append object to end
|
| clear(...)
| L.clear() -> None -- remove all items from L
|
| copy(...)
| L.copy() -> list -- a shallow copy of L
|
| count(...)
| L.count(value) -> integer -- return number of occurrences of value
|
| extend(...)
| L.extend(iterable) -> None -- extend list by appending elements from the iterable
|
| index(...)
| L.index(value, [start, [stop]]) -> integer -- return first index of value.
| Raises ValueError if the value is not present.
|
| insert(...)
| L.insert(index, object) -- insert object before index
|
| pop(...)
| L.pop([index]) -> item -- remove and return item at index (default last).
| Raises IndexError if list is empty or index is out of range.
|
| remove(...)
| L.remove(value) -> None -- remove first occurrence of value.
| Raises ValueError if the value is not present.
|
| reverse(...)
| L.reverse() -- reverse *IN PLACE*
|
| sort(...)
| L.sort(key=None, reverse=False) -> None -- stable sort *IN PLACE*
|
| ----------------------------------------------------------------------
| Data and other attributes defined here:
|
| __hash__ = None
元组tuple
Python的元组与列表类似,不同之处在于元组的元素不能修改;元组使用小括号(),列表使用方括号[];元组创建很简单,只需要在括号中添加元素,并使用逗号(,)隔开即可。
元组初始化
>>> t = (1, 2, 3)
>>> print(t)
(0, 1, 2)
>>> t = tuple(range(3))
>>> print(t)
(0, 1, 2)
元组函数
关于tuple相关的函数可以使用help命令获得。
help(tuple)
Help on class tuple in module builtins:
class tuple(object)
| tuple() -> empty tuple
| tuple(iterable) -> tuple initialized from iterable's items
|
| If the argument is a tuple, the return value is the same object.
|
| Methods defined here:
|
| count(...)
| T.count(value) -> integer -- return number of occurrences of value
|
| index(...)
| T.index(value, [start, [stop]]) -> integer -- return first index of value.
| Raises ValueError if the value is not present.
list和index方法的使用和list一模一样。
命名元组
Python有一个类似tuple的容器namedtuples(命名元组),位于collection模块中。namedtuple是继承自tuple的子类,可创建一个和tuple类似的对象,而且对象拥有可访问的属性。
在c/c++中,对应的数据类型是结构体struct。
struct Point//声明一个结构体类型Point,代表一个点
{
int x; //包括一个整型变量x
int y; //包括一个整型变量y
}; //最后有一个分号
这样就声明了一个新的结构体类型Point,有了类型就可以定义结构体的变量了。
Point p1,p2;
在c/c++中结构体的最大作用在于组织数据,也就是对数据的封装(可以把结构体理解为特殊的类)。在python中起相同作用的就是命名元组了。命名元祖的具体使用如下
>>> from collections import namedtuple #依赖collections包的namedtuple模块
>>> Point = namedtuple('Point', 'x,y')
>>> p1 = Point(11, y=22)
>>> p1
Point(x=11, y=22)
>>> type(p1)
__main__.Point
>>> p1.x
11
>>> p1.y
22
>>> p1[0] + p1[1]
33
>>> a, b = p1
>>> a, b
(11, 22)
命名元祖的具体使用可以参见:namedtuple()以及python 命名元组
字典dict
字典相关的所有内容如下
xmind文件可点这里下载
help(dict)
可以发现,dict是python內建的类,是一种key-value结构
Help on class dict in module __builtin__:
class dict(object)
| dict() -> new empty dictionary
| dict(mapping) -> new dictionary initialized from a mapping object's
| (key, value) pairs
| dict(iterable) -> new dictionary initialized as if via:
| d = {}
| for k, v in iterable:
| d[k] = v
| dict(**kwargs) -> new dictionary initialized with the name=value pairs
| in the keyword argument list. For example: dict(one=1, two=2)
|
| Methods defined here:
字典(dictionary)是除列表之外python中最灵活的内置数据结构类型。列表是有序的对象结合,字典是无序的对象集合。两者之间的区别在于:字典当中的元素是通过键来存取的,而不是通过偏移存取。
dict = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'};
每个键与值必须用冒号隔开(:),每对用逗号分割,整体放在花括号中({})。键必须独一无二,但值则不必;值可以取任何数据类型,但必须是不可变的,如字符串,数或元组。
字典初始化
In [1]: d = {} #{}被字典占用了,所以set不能按照这个初始化
In [2]: type(d)
Out[2]: dict
In [3]: d = dict()
In [4]: d = {'a':1, 'b':2}
In [5]: d = dict([('a', 1), ('b', 2)]) #可接受以元组为元素的列表
In [6]: d
Out[6]: {'a': 1, 'b': 2}
In [7]: d = dict.fromkeys(range(5)) # 传入的可迭代元素为key, 值为None
In [8]: d
Out[8]: {0: None, 1: None, 2: None, 3: None, 4: None}
In [9]: d = dict.fromkeys(range(5), 'abc') # 传入的可迭代元素为key, 值为'abc'
In [10]: d
Out[10]: {0: 'abc', 1: 'abc', 2: 'abc', 3: 'abc', 4: 'abc'}
增加
d[key] = value
,update
In [10]: d
Out[10]: {0: 'abc', 1: 'abc', 2: 'abc', 3: 'abc', 4: 'abc'}
In [11]: d['a'] = 1 # 可以直接使用key作为下标, 对某个不存在的下标赋值,会增加kv对
In [12]: d
Out[12]: {0: 'abc', 1: 'abc', 2: 'abc', 3: 'abc', 4: 'abc', 'a': 1}
In [13]: d.update([('c',3),('p',0)]) # update 传入的参数需要和dict保持一致
In [14]: d
Out[14]: {0: 'abc', 1: 'abc', 2: 'abc', 3: 'abc', 4: 'abc', 'p': 0, 'c': 3, 'a': 1}
In [15]: d.update([('c', 4), ('p', 4)]) #对已经存在的update时会进行修改,通常用于合并字典
In [16]: d
Out[16]: {0: 'abc', 1: 'abc', 2: 'abc', 3: 'abc', 4: 'abc', 'p': 4, 'c': 4, 'a': 1}
删除
In [16]: d
Out[16]: {0: 'abc', 1: 'abc', 2: 'abc', 3: 'abc', 4: 'abc', 'p': 4, 'c': 4, 'a': 1}
In [17]: help(d.pop)
Help on built-in function pop:
pop(...) method of builtins.dict instance
D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
If key is not found, d is returned if given, otherwise KeyError is raised
In [18]: d.pop(0) # 删除一个key,并且返回对应的value
Out[18]: 'abc'
In [19]: d
Out[19]: {1: 'abc', 2: 'abc', 3: 'abc', 4: 'abc', 'p': 4, 'c': 4, 'a': 1}
In [20]: d.pop(0) # 如果要删除的key不存在,则抛出KeyError
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-20-e1702b259b84> in <module>()
----> 1 d.pop(0)
KeyError: 0
In [21]: d.pop(0, 'default') # 如果给定default,则删除不存在的key时会返回default
Out[21]: 'default'
In [22]: d.pop(1, 'default') # 给定的default对存在的key不会产生影响
Out[22]: 'abc'
In [23]: d
Out[23]: {2: 'abc', 3: 'abc', 4: 'abc', 'p': 4, 'c': 4, 'a': 1}
访问
单个元素的访问
In [1]: d = {'r':2, 'd':2, 'c':3, 'p':0}
In [2]: d
Out[2]: {'c': 3, 'd': 2, 'p': 0, 'r': 2}
In [3]: d['p']
Out[3]: 0
In [4]: d['c']
Out[4]: 3
In [5]: d['a']
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-5-169a40407b7f> in <module>()
----> 1 d['a']
KeyError: 'a'
In [6]: d.get('d')
Out[6]: 2
In [7]: d.get('a')
In [8]: d.get('a','default')
Out[8]: 'default'
In [9]: help(d.setdefault)
In [10]: d.setdefault('c','default')
Out[10]: 3
In [11]: d.setdefault('a','default')
Out[11]: 'default'
In [12]: d
Out[12]: {'a': 'default', 'c': 3, 'd': 2, 'p': 0, 'r': 2}
字典的遍历
直接for in
遍历
for x in d:
print(x)
直接用for in 遍历字典, 遍历的是字典的key
keys
函数遍历
for x in d.keys():
print(x)
values
函数遍历
for x in d.values():
print(x)
items
函数遍历
for x in d.items():
print(x)
输出如下
('d', 2)
('a', 'default')
('c', 3)
('r', 2)
('p', 0)
另外一种方式:解析(k,v)对
for k, v in d.items():
print(k, v)
输出如下
d 2
a default
c 3
r 2
p 0
keys, values, items 返回的都类似生成器的对象, 它并不会复制一份内存
Python2对应的函数返回的是列表, 会复制一份内存
字典的限制
默认字典
默认字典是defaultdict
In [25]: from collections import defaultdict
In [26]: d1 = {}
In [27]: d1
Out[27]: {}
In [28]: d2 = defaultdict(list) # list在此是list的初始化函数
In [29]: d2
Out[29]: defaultdict(list, {})
In [30]: d1['a']
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-30-a9ea8faf9ae0> in <module>()
----> 1 d1['a']
KeyError: 'a'
In [31]: d2['a']
Out[31]: []
default初始化的时候, 需要传入一个工厂函数, 具体的介绍可以使用help(defaultdict)来查看,当我们使用下标访问一个key的时候, 如果这个key不存在, defaultdict会自动调用初始化时传入的函数, 生成一个对象作为这个key的value。因此上面的list函数初始化的时候就生成了一个空列表。
以下是使用dict和defaultdict的对比
d = {}
for k in range(10):
for v in range(10):
if k not in d.keys():
d[k] = []
d[k].append(v)
如果这段代码使用defaultdict来写
d = defaultdict(list)
for k in range(10):
for v in range(10):
d[k].append(v)
有序字典
有序字典是OrderedDict(第一个字母大写)
In [33]: from collections import OrderedDict
In [34]: d = OrderedDict()
In [35]: d[0] = 3
In [36]: d[3] = 4
In [37]: d[1] = 5
In [38]: d
Out[38]: OrderedDict([(0, 3), (3, 4), (1, 5)])
In [39]: for k, v in d.items():
...: print(k, v)
...:
0 3
3 4
1 5
有序字典会保持插入的顺序
集合set
集合相关的所有内容如下
xmind文件可点这里下载
>>> help(set)
Help on class set in module __builtin__:
class set(object)
| set() -> new empty set object
| set(iterable) -> new set object
|
| Build an unordered collection of unique elements.
|
| Methods defined here:
详细使用可以参考:Python集合(set)类型的操作_农村的我的博客-CSDN博客_python set长度
下面是一个小例子:
>>> a = [11,22,33,44,11,22]
>>> b = set(a)
>>> b
set([33, 11, 44, 22])
>>> c = [i for i in b]
>>> c
[33, 11, 44, 22]
定义与初始化
In [2]: s = set()
In [3]: s
Out[3]: set()
In [4]: s = {1, 2, 3}
In [5]: s
Out[5]: {1, 2, 3}
In [6]: s = set(range(3))
In [7]: s
Out[7]: {0, 1, 2}
增加
增加函数有两个add
和update
add
是增加单个元素,和列表的append操作类似,是原地修改
update
是增加一个可迭代对象,和列表的extend操作类似,是原地修改
两个函数对于已经存在的元素会什么也不做
In [7]: s
Out[7]: {0, 1, 2}
In [8]: s.add(3)
In [9]: s
Out[9]: {0, 1, 2, 3}
In [10]: s.add(3)
In [11]: s
Out[11]: {0, 1, 2, 3}
In [12]: help(s.update)
Help on built-in function update:
update(...) method of builtins.set instance
Update a set with the union of itself and others.
In [13]: s.update(range(4, 7))
In [14]: s
Out[14]: {0, 1, 2, 3, 4, 5, 6}
In [15]: s.update(range(4, 9))
In [16]: s
Out[16]: {0, 1, 2, 3, 4, 5, 6, 7, 8}
删除
In [16]: s
Out[16]: {0, 1, 2, 3, 4, 5, 6, 7, 8}
In [17]: s.remove(0)
In [18]: s
Out[18]: {1, 2, 3, 4, 5, 6, 7, 8}
In [19]: s.remove(10)
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-19-99f2b84d3df8> in <module>()
----> 1 s.remove(10)
KeyError: 10
In [21]: s
Out[21]: {1, 3, 4, 5, 6, 7, 8}
In [22]: s.discard(1)
In [23]: s
Out[23]: {3, 4, 5, 6, 7, 8}
In [24]: s.discard(10)
In [25]: s
Out[25]: {3, 4, 5, 6, 7, 8}
In [26]: help(s.pop)
Help on built-in function pop:
pop(...) method of builtins.set instance
Remove and return an arbitrary(随机的) set element.
Raises KeyError if the set is empty.
In [27]: s.pop()
Out[27]: 3
In [28]: s.pop()
Out[28]: 4
In [29]: s.clear()
In [30]: s
Out[30]: set()
In [31]: s.pop()
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-31-e76f41daca5e> in <module>()
----> 1 s.pop()
KeyError: 'pop from an empty set'
修改
集合不能修改单个元素
###查找
集合的pop操作的随机性可以证明集合不是线性结构的。
In [32]: s = {1, 2, 3, 4, 5, 65, 66, 67, 88}
In [33]: s.pop()
Out[33]: 65
成员运算符
用于判断一个元素是否在容器中
In [34]: 1 in [1, 2, 3, 4]
Out[34]: True
In [35]: 5 in [1, 2, 3, 4]
Out[35]: False
In [36]: 5 not in [1, 2, 3, 4]
Out[36]: True
In [37]: 'love' in 'I love python'
Out[37]: True
In [38]: [1, 2] in [1, 2, 3, 4]
Out[38]: False
In [39]: 1 in (1, 2, ,3 ,4)
File "<ipython-input-39-ed83805ebe55>", line 1
1 in (1, 2, ,3 ,4)
^
SyntaxError: invalid syntax
In [40]: 1 in (1, 2, 3, 4)
Out[40]: True
In [41]: 1 in {1, 2, 3, 4}
Out[41]: True
集合的成员运算和其他线性结构的时间复杂度不同
In [42]: lst = list(range(100000))
In [43]: s = set(range(100000))
In [44]: %%timeit
...: -1 in lst
...:
1000 loops, best of 3: 1.61 ms per loop
In [45]: %%timeit
...: -1 in s
...:
The slowest run took 29.72 times longer than the fastest. This could mean that an intermediate result is being cached.
10000000 loops, best of 3: 49.3 ns per loop
由以上可见,做成员运算的时候 集合的效率远高于列表。
In [46]: lst2 = list(range(100))
In [47]: s = set(range(100))
In [48]: %%timeit
...: -1 in lst2
...:
1000000 loops, best of 3: 1.6 µs per loop
In [49]: %%timeit
...: -1 in s
...:
The slowest run took 20.77 times longer than the fastest. This could mean that an intermediate result is being cached.
10000000 loops, best of 3: 56.9 ns per loop
做成员运算时 列表的效率和列表的规模有关,而集合的效率和集合的规模无关。
成员运算:
集合运算
集合运算主要有:交集,差集,对称差集,并集
python中的集合运算都对应两个版本,一个默认版本(返回新的集合),一个update版本(会更新集合本身)。
交集
intersection
交集的特性:满足交换律,重载了&
运算符
In [50]: s1 = {1, 2, 3}
In [51]: s2 = {2, 3, 4}
In [52]: s1.intersection(s2)
Out[52]: {2, 3}
In [53]: s2.intersection(s1) #交集满足交换律
Out[53]: {2, 3}
In [54]: s1
Out[54]: {1, 2, 3}
In [55]: s2
Out[55]: {2, 3, 4}
In [56]: s1.intersection_update(s2)
#交集的update版本,做原地修改,返回none,相当于 s1 = s1.intersection(s2)
In [57]: s1
Out[57]: {2, 3}
In [58]: s2
Out[58]: {2, 3, 4}
In [59]: s1 = {1, 2, 3}
In [60]: s1 & s2 #交集重载了&运算符,相当于 s1.intersection(s2)
Out[60]: {2, 3}
差集
difference
差集特性:不满足交换律,重载了-
运算符
In [61]: s1 = {1, 2, 3}
In [62]: s2 = {2, 3, 4}
In [63]: s1.difference(s2)
Out[63]: {1}
In [64]: s2.difference(s1) #差集不满足交换律
Out[64]: {4}
In [65]: s1.difference_update(s2) #差集的update版本,相当于 s1 = s1.difference(s2)
In [66]: s1
Out[66]: {1}
In [67]: s1 = {1, 2, 3}
In [68]: s1 - s2 #差集重载了-运算符,相当于 s1.difference(s2)
Out[68]: {1}
In [69]: s2 - s1
Out[69]: {4}
####对称差集
symmetric_difference
对称差集特性:满足交换律,重载了^
运算符
In [70]: s1 = {1, 2, 3}
In [71]: s2 = {2, 3, 4}
In [72]: s1.symmetric_difference(s2)
Out[72]: {1, 4}
In [73]: s2.symmetric_difference(s1) #对称差集满足交换律
Out[73]: {1, 4}
In [74]: s1.symmetric_difference_update(s2)
#对称差集的update版本,相当于 s1 = s1.symmetric_difference(s2)
In [75]: s1
Out[75]: {1, 4}
In [76]: s1 = {1, 2, 3}
In [77]: s1 ^ s2 #对称差集重载了^运算符,相当于 s1.symmetric_difference(s2)
Out[77]: {1, 4}
并集
union
,update
并集特性:满足交换律,重载了|
运算符
In [78]: s1 = {1, 2, 3}
In [79]: s2 = {2, 3, 4}
In [80]: s1.union(s2)
Out[80]: {1, 2, 3, 4}
In [81]: s2.union(s1) #并集满足交换律
Out[81]: {1, 2, 3, 4}
In [82]: s1.update(s2) #update函数就是并集的update版本,相当于 s1 = s1.update(s2)
In [83]: s1
Out[83]: {1, 2, 3, 4}
In [84]: s1 = {1, 2, 3}
In [85]: s1 + s2 #并集重载的运算符不是+
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-85-1659087814e1> in <module>()
----> 1 s1 + s2
TypeError: unsupported operand type(s) for +: 'set' and 'set'
In [86]: s1 | s2 #并集重载的运算符是|
Out[86]: {1, 2, 3, 4}
集合相关的判断
issuperset
,issubset
isdisjoint
:判断是否两个集合是否不相交(disjoint),有交集则返回False,没有交集则返回True
In [87]: s1 = {1, 2, 3, 4}
In [88]: s2 = {1, 2}
In [89]: s1.issuperset(s2) #判断是否是超集
Out[89]: True
In [90]: s2.issubset(s1) #判断是否是子集
Out[90]: True
In [91]: s1.isdisjoint(s2) #判断是否不相交
Out[91]: False
In [92]: s1 = {1, 2}
In [93]: s2 = {3, 4}
In [94]: s1.isdisjoint(s2)
Out[94]: True
集合的限制
集合的元素不能是可变的,集合的元素必须可hash
In [95]: {'a', 'b', 'c'}
Out[95]: {'a', 'b', 'c'}
In [96]: {[1, 2, 3]}
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-96-e7ef34388120> in <module>()
----> 1 {[1, 2, 3]}
TypeError: unhashable type: 'list'
In [97]: {bytearray(b'abc')}
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-97-62b530a8195b> in <module>()
----> 1 {bytearray(b'abc')}
TypeError: unhashable type: 'bytearray'
In [98]: {{3, 4}}
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-98-47b1c90a198f> in <module>()
----> 1 {{3, 4}}
TypeError: unhashable type: 'set'
In [99]: {(1, 2)}
Out[99]: {(1, 2)}
In [100]: {b'abc'}
Out[100]: {b'abc'}
#hash函数可以直接使用
In [101]: hash(b'abc')
Out[101]: 1955665834644107130
In [102]: hash([1, 2, 3])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-102-0b995650570c> in <module>()
----> 1 hash([1, 2, 3])
TypeError: unhashable type: 'list'
常用内置函数
abs(x)
返回一个数的绝对值。参数可以是普通的整数,长整数或者浮点数。如果参数是个复数,返回它的模。
>>> abs(1)
1
>>> abs(-1)
1
divmod(x,y)
结合除法和余数运算,返回包含商和余数的元组
>>> divmod(5,2)
(2, 1)
pow(x, y[, z])
pow(x,y)返回x的y次方,如果z存在即pow(x,y,z),其结果等于pow(x,y)%z
>>> pow(2,3)
8
>>> pow(2,3,3)
2
>>> pow(2,-3)
0.125
round(x[, n])
返回浮点数x的四舍五入的值,其中n值,表示小数点后的位数。
>>> round(2)
2.0
>>> round(2.567)
3.0
>>> round(2.567, 2)
2.57
min(x[,y,z…]), max(x[,y,z…])
返回给定参数和最小值或者最大值,参数可以为序列。
>>> min(2,1)
1
>>> max(2,1)
2
sum(iterable[,start])
sum()函数对序列进行求和运算
>>> sum([1,2,3,4])
10
>>> sum(range(101))
5050
oct(x)
oct()函数用于将一个整数转换为八进制
>>> oct(0)
'0'
>>> oct(4)
'04'
>>> oct(8)
'010'
>>> oct(10)
'012'
hex(x)
hex()函数用于将10进制数转换为16进制数
>>> hex(255)
'0xff'
>>> hex(256)
'0x100'
>>> hex(16)
'0x10'
chr(i)
char()函数根据输入的数字返回一个字符,数字的范围为range(0,256)
>>> chr(65)
'A'
bin(x)
bin()函数返回一个整数的二进制表示。
>>> bin(1)
'0b1'
>>> bin(10)
'0b1010'
类型转换
bool([x])
bool()函数将参数转换为bool类型,如果没有给参数则返回False
>>> bool()
False
>>> bool(0)
False
>>> bool(1)
True
int(x,base=10)
int()函数用于将一个字符串或者数字转换为整型。
参数base表示进制数,默认为十进制
>>> int()
0
>>> int(1.1)
1
>>> int('10',16)
16
>>> int('10',8)
8
float([x])
float()函数用于将整数和字符串转换成浮点数
>>> float()
0.0
>>> float(1)
1.0
str(object)
str()函数将对象对象转换为可阅读的样式。
>>> str()
''
>>> str(str)
"<type 'str'>"
>>> str('1234')
'1234'
list([iterable])
list()函数根据输入的可迭代的参数,创建一个新的列表。
>>> list()
[]
>>> list('1234')
['1', '2', '3', '4']
tuple([iterable])
tuple()函数根据输入的可迭代的参数,创建一个新的元组。
>>> tuple()
[]
>>> tuple('1234')
['1', '2', '3', '4']
dict([iterable])
dict()函数根据传入的参数,创建一个新的字典。
>>> dict()
{}
>>> dict(a=1,b=2,c=3)
{'a': 1, 'c': 3, 'b': 2}
>>> dict(zip(['a','b','c'],['1','2','c']))
{'a': '1', 'c': 'c', 'b': '2'}
set([iterable])
set()函数根据参数创建一个新的集合
>>> set()
set([])
>>> set('1234')
set(['1', '3', '2', '4'])
frozenset([iterable])
frozenset()函数返回一个冻结的set集合,其不能添加和删除元素
>>> frozenset()
frozenset([])
>>> frozenset(range(10))
frozenset([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
序列相关
len(object)
返回字符串或者序列的长度
>>> len("hello world!")
12
>>> len([1,2,3,4,5])
5
range([start,]stop[,step])
创建一个整数列表,一般用在for循环中。
其中的参数说明为:
>>> range(5)#等于range(0,5)
[0, 1, 2, 3, 4]
>>> range(1, 5)#不包含5
[1, 2, 3, 4]
>>> range(1,5,2)# 步长为2
[1, 3]
>>> range(0)# range(0,0)为空
[]
>>> range[5,1] # start不能小于end
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'builtin_function_or_method' object has no attribute '__getitem__'
xrange([start,]stop[,step])
xrange和range完全相同,使用方法相同,但是xrange生成的不是一个数组,而是一个生成器
,其只有在需要时才回生成列表值,当列表很大的时候,可以节省内存。
zip([iterable,…])
zip()函数聚合传入的一个到多个迭代器相同位置的元素生成一个新的元组类型的迭代器
>>> x = [1,2,3]
>>> y = [a,b,c]
>>> z = [4,5,6]
>>> zip(x,y)
[(1, 'a'), (2, 'b'), (3, 'c')]
>>> zip(x,y,z)
[(1, 'a', 4), (2, 'b', 5), (3, 'c', 6)]
sorted()
sorted()函数对所有可迭代的对象进行排序操作。
其原型为:sorted(iterable, cmp=None, key=None, reverse=False)
其主要参数含义为
>>> sorted([4,3,2,1]) # 默认为升序排列
[1, 2, 3, 4]
>>> sorted([1,2,3,4], reverse=False) #升序排列
[1, 2, 3, 4]
>>> sorted([1,2,3,4], reverse=True) #降序排列
[4, 3, 2, 1]
reversed(seq)
reversed()反转序列,生成一个新的序列
>>> list(reversed([1,2,3,4]))
[4, 3, 2, 1]
enumerate(iterable[, start])
enumerate()函数用于将一个可遍历的数据对象,组合为一个索引序列,同时包含索引和数据。
其中参数start表示参数下标的起始位置。
>>> a = [1,2,3,4]
>>> list(enumerate(a))
[(0, 1), (1, 2), (2, 3), (3, 4)]
>>> list(enumerate(a,2))
[(2, 1), (3, 2), (4, 3), (5, 4)]
iter
iter()函数用于创建一个迭代器
>>> a = iter(range(10))
>>> next(a)
0
>>> next(a)
1
>>> next(a)
2
next()
next()函数返回迭代器的下一个元素
>>> a = iter(range(10))
>>> next(a)
0
>>> next(a)
1
>>> next(a)
2
slice
slice()函数返回一个切片对象,主要用在切片操作函数里的参数传递。
slice0_5 = slice(5)#返回0-5切片的数据
slice4_10 = slice(4,10)#返回4-10切片的数据
print range(10)[slice0_5]#[0, 1, 2, 3, 4]
print range(10)[slice4_10]#[4, 5, 6, 7, 8, 9]
all(iterable)
all()函数用于判断给定的可迭代参数中是否含有元素为0、’’、False,如果含有这样的元素
返回False否则返回True,如果是空的迭代器则返回True。
>>> all([])#空迭代器
True
>>> all([1,2,3,4])#所有元素都不为0、''、False
True
>>> all([0,1,2,3,4])#含有元素 0
False
>>> all(['',1,2,3,4])# 含有元素 ''
False
>>> all([False,1,2,3,4])# 含有元素 False
False
any(iterable)
any()函数用于判断给定的迭代器是否全部为空的元素0、’’、False,
如果全部为空则返回False,否则返回True。
空的元组或者列表返回False.
>>> any([]) #空列表返回False
False
>>> any([1]) #没有空元素
True
>>> any([0,1]) #存在非空元素
True
>>> any([0]) #不存在非空元素
False
all()和any()的函数的区别如下:
cmp(x,y)
cmp()函数用于比较两个对象的大小,如果x<y返回-1,如果相等则返回0,如果x>y则返回1.
>>> cmp(1,2)
-1
>>> cmp(1,1)
0
>>> cmp(2,1)
1
filter(function or None, sequence)
filter()函数用于过滤序列,过滤不符合的数据,返回有符合元素组成的序列。
#-*-coding:utf-8 -*-
#过滤大于5的数
def max5(s) :
return s > 5
print filter(max5, range(10))
map
map()函数将传入的函数应用到序列中的所有的项。可以应用多个序列,
当传入的为None而不是函数的时候,map()函数将序列中的元素合并起来,返回一个新的元祖
map(function, sequence[, sequence, …])
def add(a,b):
return a+b
print map(add, [1,2],[3,4])
print map(None, [1,2],[3,4])
输出的值为:
[4, 6]
[(1, 3), (2, 4)]
对象操作
help([object])
help()函数用于查看函数或模块用途的详细说明
dir([object])
dir()函数主要是收集对象的信息。如果其不带参数,就返回当前范围的变量、方法和定义的类型列表;
带参数的时候,返回参数的属性,方法列表。
>>> dir()#返回当前的属性列表
['__builtins__', '__doc__', '__name__', '__package__']
>>> dir(int)#返回int的属性列表
['__abs__', '__add__', '__and__', '__class__', '__cmp__', '__coerce__', '__delattr__', '__div__', '__divmod__', '__doc__', '__float__', '__floordiv__', '__format__', '__getattribute__', '__getnewargs__', '__hash__', '__hex__', '__index__', '__init__', '__int__', '__invert__', '__long__', '__lshift__', '__mod__', '__mul__', '__neg__', '__new__', '__nonzero__', '__oct__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdiv__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'imag', 'numerator', 'real']
id([object])
id()函数用于获取对象的内存地址。
>>> id(int)
4553319656
>>> id('1234')
4555435104
hash([object])
hash()用于获取一个对象的哈希值
>>> hash(str)
284584308
>>> hash("1234")
659471584116336832
type(name,bases,dict)
type()函数,如果只有一个参数则返回对象的类型,如果有三个参数则返回新的类对象
>>> type(1)
<type 'int'>
>>> type({1:'value'})
<type 'dict'>
>>> x = type('X',(object,), dict(a=1))
>>> x
<class '__main__.X'>