你的分享就是我们的动力 ---﹥

Python小白的新手教程(一) -- 【网络课堂】

时间:2016-11-18 18:31来源:www.chengxuyuans.com 点击:

 本文是 python 入门级别的基础知识,包括数据类型和变量、输入输出、字符串和编码、list tuple dict set 、条件判断、循环、函数、切片 迭代 列表生成器 生成器 迭代器等。

参考课程:

廖雪峰的官方网站  慕课网-python入门

参考书籍:

侯爵.《编程小白的第1本 Python 入门书》  


 

前言

Python 是一种相当高级的语言,通过 Python 解释器把符合语法的程序代码转换成 CPU 能够执行的机器码,然后执行。Python 非常简洁,完成同一个任务,C 语言要写  1000 行代码,Java 只需要写 100 行,而 Python 可能只要 20 行,但是 Python 的运行速度稍慢。

安装 python 后可以通过文本编辑器来保存代码 .py,通过命令行模式来运行代码,而 Python 自带的 IDLE(交互模式)可以用来验证代码。也可以直接用 IDE 集成开发环境来编写和运行代码。

第一部分 Python基础

数据类型和变量、输入输出、字符串和编码、list tuple dict set 、条件判断、循环、函数、切片 迭代 列表生成器 生成器 迭代器

1. Python 用 # 来注释,用缩进表示语法。Python 区分大小写。

    缩进按照约定俗成的习惯,坚持使用 4 个空格的缩进(把 tab 设置为 4 个空格)。

优点:强迫写出缩进较小的代码,把一段很长的代码拆分成若干函数。
缺点:“复制-粘贴”功能失效,需要在 IDLE 上重新检查缩进。

1. 数据类型、常变量、四种运算

2. 字符串和编码

3. 输入和输出

3.1 输出

print('hello,world')
print('100+300=',100+300) 
print('hello ,','world')   
print('hello,'+'world') 
# 结果:hello,world 
print('bang ! '*3)
# 结果:bang ! bang ! bang !
print(type(2))
# 结果:<class 'int'> 
search = '168'
num_a = '138-6168-0006'
num_b = '168-1222-0006'
print(search + ' is at ' + str(num_a.find(search)) + ' to ' + str(num_a.find(search)+len(search)) + ' of num_a ')
print(search + ' is at ' + str(num_b.find(search)) + ' to ' + str(num_b.find(search)+len(search)) + ' of num_b ')
# 结果:
168 is at 5 to 8 of num_a
168 is at 0 to 3 of num_b

  2. 输出多个字符串用逗号 "," 隔开,print() 依次打印每个字符串,遇到逗号 ","会输出一个空格。

  3. 在 print() 函数中,"+" 在整数和浮点数中为运算符,在字符型中为连接符。字符串与整数、浮点数一起输出必须用 ",",不能用 "+" 。

3.2 输入

name=input('please enter your name')
print('hello,',name)

  4. 在 input() 函数中添加字符串可以提示用户输入内容。

3.3 格式化

3.3.1 用 % 格式化

  5.  %d 整数   %f 浮点数    %s 字符串    %x 十六进制整数。

print( 'Hello, %s' % 'world')
print('Hi, %s, you have $%d.') % ('Michael', 1000000)
print('%2d-%02d' % (3, 1))   # 结果:3-01
print( '%.2f' % 3.1415926)    # 结果:3.14

  6. 有几个 %? 占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个 %?,括号可以省略。

  7. 格式化整数和浮点数可以指定是否补 0 和整数与小数的位数。

  8. 如果不太确定应该用什么,%s 永远起作用,它会把任何数据类型转换为字符串。

  9. 字符串里面的 % 是一个普通字符,用 %% 来表示一个 % 。

3.3.2 用 format 格式化

print('Hello, {}'.format('world'))
print('Hi, {}, you have ${}.'.format ('Michael', 1000000)) 

4. list 和 tuple

4.1 列表 list []

  10. list 是一种有序的集合,可以随时添加和删除其中的元素。 

classmates = ['Michael', 'Bob', 'Tracy']
print(classmates)  # 结果:['Michael', 'Bob', 'Tracy']

  11. 用 len() 函数可以获得 list 元素的个数。

print(len(classmates))  # 结果:3

  12. 顺序索引、逆序索引。

print(classmates[0],classmates[1],classmates[2])  # 结果:Michael Bob Tracy
print(classmates[-1], classmates[-2],classmates[-3])  # 结果:Tracy Bob Michael

  13. 在 list 末尾添加元素、在 list 指定位置添加元素、添加多个元素。

classmates.append('Adam')
print(classmates)  # 结果:['Michael', 'Bob', 'Tracy', 'Adam']  
classmates.insert(1, 'Jack')
print(classmates)  # 结果:['Michael', 'Jack', 'Bob', 'Tracy']  
classmates = ['Michael', 'Bob', 'Tracy']
classmates.extend(['Adam', 'Jack'])
print(classmates)
# 结果:['Michael', 'Bob', 'Tracy', 'Adam', 'Jack']

  14. 删除 list 末尾的元素、删除指定位置的元素。

classmates.pop()
print(classmates)  # 结果:['Michael', 'Bob']
classmates.pop(1)
print(classmates)  # 结果:['Michael', 'Tracy'] 
classmates = ['Michael', 'Bob', 'Tracy']
classmates.remove('Bob')    # 用 remove 删除要指定元素
print(classmates)
classmates = ['Michael', 'Bob', 'Tracy']
del classmates[2]
print(classmates)

  15. 更新某个元素。

classmates[1] = 'Sarah'
print(classmates)  # 结果:['Michael', 'Sarah', 'Tracy']

  16. list 中可以包含各种数据类型。

 L = ['Apple', 123, True]

  17. 嵌套 list。

s = ['python', 'java', ['asp', 'php'], 'scheme']
print(len(s))    # 结果:4
print(s[2][0])  # 结果:asp

4.2 元组 tuple ()

  18. tuple 初始化后不能修改,其它同 list ( len()、索引、可以是任何数据类型、嵌套)。

classmates = ('Michael', 'Bob', 'Tracy')

  19. 只有1个元素的 tuple 定义时必须加一个逗号 “,” ,来消除歧义(数学计算意义上的括号)。

t = (1,)
print(t)  # 结果:(1,)

  20. "可变的"tuple (嵌套) 。

t = ('a', 'b', ['A', 'B'])
t[2][0] = 'X'
t[2][1] = 'Y'
print(t)  # 结果:('a', 'b', ['X', 'Y'])

  21. tuple 所谓的"不变"是说 tuple 的每个元素指向永远不变,即指向 'a' ,不能改成指向 'b' ,指向一个 list ,不能改成指向其他对象,但指向的这个 list 本身是可变的。要创建一个内容也不变的 tuple ,就必须保证 tuple 的每一个元素本身也不能变。

                                     

5. dict 和 set

5.1 字典 dic {}

  22. 内置的字典,全称dictionary,在其他语言中称为map,使用键-值(key-value)存储,具有极快的查找速度。

d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
print(d['Michael'])  # 结果:95

  23. 用 len() 函数可以获得 dict 元素的个数。

d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
print(len(d))  # 结果:3

  24. 添加 key-value 、添加多个 key-value。

d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
d['Adam']=67
print(d)  # 结果:{'Adam': 67, 'Bob': 75, 'Tracy': 85, 'Michael': 95}
d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
d.update({'Adam':67,'Jack':99})
print(d)

  25. 更新 value 。

d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
d['Bob'] = 90
print(d['Bob'])  # 结果:90

  26. 删除 key-value 。dict 没有 remove

d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
d.pop('Bob')
print(d)  # 结果:{'Michael': 95, 'Tracy': 85} 
d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
del d['Bob']
print(d)

  27. 检查 key 是否在 dict 中。

d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
print('Thomas' in d)  # 结果:False

d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
print('Michael' in d)  # 结果:True
d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
print(d.get('Thomas'))  # 结果:None

d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
print(d.get('Michael'))  # 结果:95

  28. dict 查找和插入快,占用空间大,dict 无序;list 查找和插入慢,占用空间小,list 有序;

       dict 的 key 必须是不可变对象 (字符串、整数等都是不可变的,list 是可变的)。

5.2 set 

  29. set 和 dict 类似,也是一组 key 的集合,但不存储 value。由于 key 不能重复,所以,在 set 中,没有重复的 key 。

  30. set 自动过滤重复的元素, set 中的元素是无序的。

s = set([2,1,2,3])
print(s)  # 结果:{1, 2, 3}

#
{1, 2, 3} 只是告诉你这个 set 内部有 1,2,3 这 3 个元素,显示的顺序并不表示 set 是有序的

  31. 添加元素。

s = set([2,1,2,3])
s.add(4)
print(s)  # 结果:{1, 2, 3, 4}

  32. 删除元素。set 没有 del pop 。

s = set([2,1,2,3,4])
s.remove(4)
print(s)  # 结果:{1, 2, 3}  
s = {1,2,3,4}
s.discard(4)
print(s)

  33. 检查 key 是否在 set 中。

s = set(['Adam', 'Lisa', 'Bart', 'Paul'])
print('adam' in s)    # 结果:False
print('Adam' in s)  # 结果:True

  34. set 可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的并、交、差操作。

s1 = set([1, 2, 3])
s2 = set([2, 3, 4])
s3 = set([1, 2, 3, 4, 5])
print(s1 & s2)   # 交      结果:{2, 3}
print(s1 | s2)   # 并      结果:{1, 2, 3, 4}
print(s3 - s1)   # 差      结果:{4, 5}  

  35. set 和 dict 的唯一区别仅在于没有存储对应的 value ,但是,set 的原理和 dict 一样。set 不可以放入可变对象,因为无法判断两个可变对象是否相等,也就无法保证set 内部 “不会有重复元素”。

 5.3 再议不可变对象

a = [6,74,2,3,5,36,5]
a.sort(reverse=True)
print(a)
# 结果:[74, 36, 6, 5, 5, 3, 2]
a = ['a', 'd', 'c']
a.sort()
print(a)  # 结果:['a', 'c', 'd']
a = 'abc'
a.replace('a','A')
print(a)  # 结果:abc 
a = 'abc'
b = a.replace('a','A')
print(a)    # 结果:abc
print(b)    # 结果:Abc

  当我们调用 a.replace('a','A') 时,实际上调用方法 replace 是作用在字符串对象 'abc' 上的,而这个方法虽然名字叫 repalce,但却没有改变字符串 'abc' 的内容。相反,replace 方法创建了一个新字符串 'Abc' 并返回。如果我们用变量 b 指向该新字符串,则变量 a 仍指向原有的字符串 'abc',但变量 b 却指向新字符串 'Abc' 了。

6. 条件判断

比较运算符 ==, !=,>, <, <=, >=
成员运算符 in,not in
身份运算符 is,is not
布尔元素符 not,and, or

  36.不同类型的对象不能使用 >, <, <=, >= 进行比较,但可以使用 ==, != 比较。浮点数和整数可以比较大小。布尔类型可以比较大小,False 为 0,True 为 1。

a = 'a'
b = 'a'
print(a is b)
print(a is not b)
# 结果:
True
False
age = 20
if age >= 6:
    print('teenager')
elif age >= 18:
    print('adult')
else:
    print('kid')

  36. 如果 if 语句判断是 True ,就执行缩进的语句。注意 if、else、elif 后面的 ":" 。

  37. if 语句从上往下判断,如果在某个判断上是 True ,把该判断对应的语句执行后,就忽略掉剩下的 elif 和 else。

  38. if 判断条件可以简写,非零数值、非空字符串、非空 list tuple dict set 等,都判断为 True,否则为 False 。

x = 8
if x:
    print('True')

  39. input() 返回的数据类型是 str ,str不能直接和整数比较,要把 str 用 int() 转为整数。

s = input('birth: ')
birth = int(s)
if birth < 2000:
    print('00前')
else:
    print('00后')

  当 if 后面的布尔表达式过长或者难于理解,可以采取给变量赋值的办法来储存布尔表达式返回的布尔值 True 或 False 。

password_list = ['*#*#', '12345']
def account_login():
    password = input('Password')
    password_correct = password == password_list[-1]
    password_reset = password == password_list[0]
    if password_correct :
        print('Login success!')
    elif password_reset :
        new_password = input('Enter a new password:')
        password_list.append(new_password)
        print('Your password has changed successfully')
        account_login()
    else:
        print('Wrong password or invalid input!')
        account_login()
account_login()

7. 循环

7.1 for...in 循环,遍历 list、tuple、disc、set 

  40. for x in y 循环就是把 y 中每个元素代入变量 x ,然后执行缩进块的语句。

names = ['Michael', 'Bob', 'Tracy']
for name in names:
    print(name)
# 对list中的每一个元素,如果在set中,就将其删除,如果不在set中,就添加进去
s = set(['Adam', 'Lisa', 'Paul'])
L = ['Adam', 'Lisa', 'Bart', 'Paul']
for a in L:
    if a in s :
        s.remove(a)
    else:
        s.add(a)
print(s)
# 遍历如下的dict,打印 name: score 
d = {'Adam': 95,'Lisa': 85,'Bart': 59}
for key in d :
    print(key+':',d[key])
# 遍历如下的set,打印出 name: score
s = set([('Adam', 95), ('Lisa', 85), ('Bart', 59)])
for x in s:
    print(x[0] + ':', x[1])

  41. range() 函数生成整数序列,list() 函数把整数序列转换为 list。 [ set()、tuple() 同 ]

        例如 range(5) 生成从0开始小于5的整数序列。

print(list(range(5)))  # 结果:[0, 1, 2, 3, 4]
print(set(range(5)))   # 结果:{0, 1, 2, 3, 4}
sum = 0
for x in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
    sum = sum + x
print(sum)
sum = 0
for x in range(101):
    sum = sum + x
print(sum)

7.2 while 循环,条件满足时进行循环,条件不满足时退出循环

# 计算100以内所有奇数之和
sum = 0
n = 99
while n > 0:
    sum = sum + n
    n = n - 2
print(sum)

7.3 break 提前退出循环

n = 1
while n <= 100:
    if n > 10: # 当n = 11时,条件满足,执行break语句
        break # break语句会结束当前循环
    print(n)
    n = n + 1
print('END')

7.4 continue 跳过当前循环,进入下一循环

n = 0
while n < 10:
    n = n + 1
    if n % 2 == 0: # 如果n是偶数,执行continue语句
        continue # continue语句会直接继续下一轮循环,后续的print()语句不会执行
    print(n)

  42.不要滥用 break 和 continue 语句。break 和 continue 会造成代码执行逻辑分叉过多,容易出错。大多数循环并不需要用到 break 和 continue 语句,上面的两个例子,都可以通过改写循环条件或者修改循环逻辑,去掉 break 和 continue 语句。

7.5 嵌套循环

for x in ['A', 'B', 'C']:
    for y in ['1', '2', '3']:
        print(x + y)
# 打印出100以内所有十位数数字比个位数数字小的数
a = set(range(10))
a.remove(0)
for x in a:
    for y in a:
        if x < y:
            print(x*10+y)
        else:
            continue

8. 函数

8.1 调用函数

Python内置了很多有用的函数,我们可以直接调用。

abs() 返回一个数的绝对值
max() 返回多个数中最大的数
cmp(x, y) 如果 x<y,返回 -1,如果 x==y,返回 0,如果 x>y,返回 1
int() 把其他数据类型转换为整数
float() 把其它数据类型转换为浮点数
str() 把其它数据类型转换为字符串
bool() 把其它数据类型转换为布尔值
hex() 把一个整数转换成十六进制表示的字符串
list()、set()、tuple() 把某序列转换为 list、set、tuple

 对函数使用别名

a = abs # 变量a指向abs函数
print(a(-1)) # 所以也可以通过a调用abs函数

8.2 定义函数

  43. 定义一个函数要使用 def 语句,依次写出函数名、括号、括号中的参数和冒号":",然后,在缩进块中编写函数体,函数的返回值用 return 语句返回。如果没有 return 语句时,自动 return None 。

def my_abs(x):
    if x >= 0:
        return x
    else:
        return -x

  44.在 Python 交互环境中定义函数时,注意 Python 会出现 ...的提示。函数定义结束后需要按两次回车重新回到 >>> 提示符下。

    

  45. 定义一个空函数,再没有想好函数代码时让整个代码能运行起来。

def nop():
    pass
# pass 也可以在其它语句
if age >= 18:
    pass

  46. 调用函数时,Python 解释器会检查内置函数的参数个数和参数类型是否正确,而对自定义的函数只检查参数个数,所以我们要在函数定义中对参数类型进行检查。数据类型检查可以用内置函数 isinstance() 实现。

def my_abs(x):
    if not isinstance(x, (int, float)):
        raise TypeError('bad operand type')
    if x >= 0:
        return x
    else:
        return -x

  47. Python 的函数返回多值其实就是返回一个 tuple 。在语法上,返回一个 tuple 可以省略括号,而多个变量可以同时接收一个 tuple ,按位置赋给对应的值。

# 给出一点的坐标,给出移动位移、角度,计算出新坐标

import math    #导入 math 包

def move(x, y, step, angle=0):
    nx = x + step * math.cos(angle)
    ny = y + step * math.sin(angle)
    return nx, ny
x, y = move(100, 100, 60, math.pi / 6)
print(x, y)          # 结果:151.96152422706632 130.0
r = move(100, 100, 60, math.pi / 6)
print(r)             # 结果:(151.96152422706632, 130.0)

8.3 函数的参数

8.3.1 位置参数

# 计算一个数的平方
def power(x):
    return x * x
# 计算一个数的n次方
def power(x, n):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

48. 位置参数,调用函数时,传入的两个值按照位置顺序依次赋给参数 x 和 n 。

8.3.2 默认参数

# 在不传入n时,n默认为2
def power(x, n=2):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

print(power(5))    # 结果:25
print(power(5,3))    # 结果:125

49.设置默认参数时,有几点要注意:

     一是必选参数在前,默认参数在后,否则Python的解释器会报错;

     二是当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。

50. 默认参数降低调用的难度。一旦需要更复杂的调用时,又可以传递更多的参数来实现。无论是简单调用还是复杂调用,函数只需要定义一个。

51. 有多个默认参数时,调用的时候,既可以按顺序提供默认参数,也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时,需要把参数名写上。

def enroll(name, gender, age=6, city='Beijing'):
    print('name:', name,'    gender:', gender,'    age:', age,'    city:', city)

enroll('Sarah','F')  # 结果:name: Sarah     gender: F     age: 6     city: Beijing
enroll('Bob','M',7)  # 结果:name: Bob     gender: M     age: 7     city: Beijing
enroll('Adam','M',city='Changsha')  # 结果:name: Adam     gender: M     age: 6     city: Changsha

  52. 默认参数必须指向不变对象。因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。

def add_end(L=[]):  # 默认参数为list,为可变对象
    L.append('END')
    return L

# 正常调用
print(add_end([1, 2, 3]))        # 结果:[1, 2, 3, 'END']
print(add_end(['x', 'y', 'z']))  # 结果:['x', 'y', 'z', 'END']

# 默认调用
print(add_end())  # 结果:['END']
print(add_end())  # 结果:['END', 'END']
print(add_end())  # 结果:['END', 'END', 'END']

# 因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]

8.3.3 可变参数

  53. 可变参数就是传入的参数个数是可变的,可以是 0 个或任意个参数,这些可变参数在函数调用时自动组装为一个 tuple 。

def calc(*numbers): # 参数前面加了一个*号。在函数内部,参数numbers接收到的是一个tuple
    sum = 0
    for n in numbers :
        sum = sum + n*n
    return sum

print(calc(1,2)) print(calc()) nums = [1, 2, 3] print(calc(nums[0], nums[1], nums[2])) print(calc(*nums)) # *nums表示把nums这个list的所有元素作为可变参数传进去

8.3.4 关键字参数

  54. 关键字参数允许你传入 0 个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个 dict 。

  55. 在调用该函数时,可以只传入必选参数。也可以传入任意个数的关键字参数。

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

person('Michael', 30)                            # 结果:name: Michael age: 30 other: {}
person('Bob', 35, city='Beijing')                # 结果:name: Bob age: 35 other: {'city': 'Beijing'}
person('Adam', 45, gender='M', job='Engineer')   # 结果:name: Adam age: 45 other: {'job': 'Engineer', 'gender': 'M'}

extra = {'city': 'Beijing', 'job': 'Engineer'}
person('Jack', 24, city=extra['city'], job=extra['job'])   # 结果:name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
person('Jack', 24, **extra)                                # 结果:name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

# **extra表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数,kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra

  56. 默认参数是所有的调用函数都有定义的所有参数(个数不变),只是默认参数的值是默认的;而关键字参数在调用函数时用户可以传入任意个数关键字参数(个数可变),只是需要含参数名,而且定义关键字参数在函数内部自动组装为一个 dict 。

8.3.5 命名关键字参数

  对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数。至于到底传入了哪些,就需要在函数内部通过 kw 检查。以 person() 函数为例,我们希望检查是否有 city 和 job 参数,但是调用者仍可以传入不受限制的关键字参数。

def person(name, age, **kw):
    if 'city' in kw:
        # 有city参数
        pass
    if 'job' in kw:
        # 有job参数
        pass
    print('name:', name, 'age:', age, 'other:', kw)

person('Jack', 24, city='Beijing', addr='Chaoyang', zipcode=123456)

  57. 命名关键字参数,只接受指定名字的关键字参数。命名关键字参数必须传入参数名。如果没有传入参数名,调用将报错(看作位置参数)。

  58. 命名关键字参数需要一个特殊分隔符 *,* 后面的参数被视为命名关键字参数,如果缺少 *,Python 解释器将无法识别位置参数和命名关键字参数。如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符 * 。

  59. 命名关键字参数可以有缺省值,从而简化调用。

def person(name, age, *, city, job):   
    print(name, age, city, job)

person('Jack', 24, city='Beijing', job='Engineer')    # 结果:Jack 24 Beijing Engineer 
def person(name, age, *args, city, job):
    print(name, age, args, city, job)

person('Jack', 24, city='Beijing', job='Engineer')    # 结果:Jack 24 () Beijing Engineer
person('Jack', 24, 1, 2, 3, city='Beijing', job='Engineer')    # 结果:Jack 24 (1, 2, 3) Beijing Engineer
def person(name, age, *, city='Beijing', job):
    print(name, age, city, job)

person('Jack', 24, job='Engineer')        # 结果:Jack 24 Beijing Engineer 

  60. 关键字参数,调用者可以传或不传关键字参数,关键字参数将在函数内部组成 dict ;命名关键字参数调用者必须传入所有的参数(定义缺省值的除外),而且需要传入参数名,但是不组成 dict 。命名关键字参数只是限制参数的名字;

8.3.6 参数组合

  61. 在 Python 中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数这 5 种参数组合使用。但参数定义的顺序必须是:参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。

def f1(a, b, c=0, *args, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)

f1(
1, 2) # 结果:a = 1 b = 2 c = 0 args = () kw = {} f1(1, 2, 3) # 结果:a = 1 b = 2 c = 3 args = () kw = {} f1(1, 2, 3, 'a', 'b') # 结果:a = 1 b = 2 c = 3 args = ('a', 'b') kw = {} f1(1, 2, 3, 'a', 'b', x=99) # 结果:a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99} args = (1, 2, 3, 4) kw = {'d': 99, 'x': '#'} f1(*args, **kw) # 结果:a = 1 b = 2 c = 3 args = (4,) kw = {'x': '#', 'd': 99}

# *args相当于传入tuple中的数字
def f2(a, b, c=0, *, d, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)

f2(
1, 2, d=99, ext=None) # 结果:a = 1 b = 2 c = 0 d = 99 kw = {'ext': None} args = (1, 2, 3) kw = {'d': 88, 'x': '#'} f2(*args, **kw) # 结果:a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}
#
若把 kw 中的第一个 key d 改为其它会报错,因为没有传入命名关键字参数

8.4递归函数

   一个函数在内部调用自身本身,这个函数就是递归函数。典型的例子就是计算阶乘。

# n! = 1 x 2 x 3 x ... x n
# (n-1)! = 1 x 2 x 3 x ... x (n-1) 
# n! = (n-1)! x n
def fact(n):
    if n==1:
        return 1
    return n * fact(n - 1)
# fact(5)计算过程
===> 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

  汉诺塔的移动可以用递归函数非常简单地实现。

def move(n, a, b, c):   #将n个盘从a柱移到到c
    if n ==1:
        print(a, '-->', c)  #a柱为一个盘时,将仅有的一块盘a柱移动到c柱
        return
    else:
        move(n-1, a, c, b)  #a柱盘多于一个时,将n-1个盘从a柱移动到b柱
        print(a, '-->', c)  #当a柱剩一个盘时,将这个盘移动到c柱
        move(n-1, b, a, c)  #将n-1盘从b柱移动到c
move(4, 'a', 'b', 'c')

9. 高级特性

9.1 切片 slice  取 list 、tuple、str 的部分元素

L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']

print(L[0:3])     # L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3
print(L[:3])      # 如果第一个索引是0,可以省略 
print(L[1:3])     #从索引1开始,取出2个元素出来
# 结果:['Michael', 'Sarah', 'Tracy']
# 结果:['Michael', 'Sarah', 'Tracy']
# 结果:['Sarah', 'Tracy']

print(L[-3:])      # 从索引-3开始开始取,一直到结束
print(L[-3:-1])    # 从索引-3开始取,直到索引-1,但不包括-1
# 结果:['Tracy', 'Bob', 'Jack']
# 结果:['Tracy', 'Bob']

print(L[0:5:2])    # 从索引0到索引4,每2个取一个
# 结果:['Michael', 'Tracy', 'Jack']

  对 list( range() ) 取部分元素。

L = list(range(100))
print(L)
print(L[:10])    # 取前十个元素
print(L[-10:])    # 取后十个元素
print(L[11:21])  # 取11-20个元素
print(L[:20:2])    # 取前20个元素,每2个取一个
print(L[::5])    #取所有元素,每5个取一个
print(L[:])      # 取所有元素

  对 tuple、str 取部分元素。

print((0, 1, 2, 3, 4)[:3])
print('ABCDEFG'[:3])
print('ABCDEFG'[::2]) 
phone_number = '138-6666-0006'
hiding_number = phone_number.replace(phone_number[:9],'*'*9)
print(hiding_number)
# 结果:*********0006

  set 、dict 不可 slice。

9.2 迭代  遍历可迭代对象

# 迭代 list tuple
L=[1, 2, 3, 4]
for a in L:
    print(a)

T=(1, 2, 3, 4)
for b in T
    print(b)
# 迭代 dict 的 key
d = {'a': 1, 'b': 2, 'c': 3}
for key in d:
    print(key)
# 结果:
b
a
c
# 迭代 dict 的 value d = {'a': 1, 'b': 2, 'c': 3} for value in d.values(): print(value) # 结果:
2
3
1
# 迭代 dict 中的 key 与 value d = {'a': 1, 'b': 2, 'c': 3} for k, v in d.items(): print('k:',v) # 结果:
b: 2
c: 3
a: 1
# 迭代字符串
for ch in 'ABC':
    print(ch)
# 结果:
A
B
C

  62. 当我们使用 for 循环时,只要作用于一个可迭代对象,for 循环就可以正常运行,而不太关心该对象究竟是 list 还是其他数据类型。可以通过 collections 模块的 Iterable 进行类型判断。

from collections import Iterable
print(isinstance('abc', Iterable))           # str是否可迭代
print(isinstance([1,2,3], Iterable))        # list是否可迭
print(isinstance({1, 2, 3},Iterable))       # set是否可迭代
print(isinstance({'a':2 ,'b':2},Iterable))    # dict是否可迭代
print(isinstance(123, Iterable))            # 整数是否可迭代
# 结果:True
# 结果:True
# 结果:True
# 结果:True
# 结果:False

  63. 如果要知道迭代的下标,可以使用内置的 enumerate 函数。

for i, value in enumerate(['A', 'B', 'C']):
    print(i, value)
# 结果:
0 A
1 B
2 C

  64. for 循环同时引用两个变量。

for x, y in [(1, 1), (2, 4), (3, 9)]:
    print(x, y)
# 结果:
1 1
2 4
3 9
for x,y in zip( ['A', 'B', 'C'],['1', '2', '3']):
    print(x + y)
# 结果:
A1
B2
C3

9.3  列表生成式  

print([x * x for x in range(1, 11)])    # 生成 [1x1, 2x2, 3x3, ..., 10x10]
print([x * x for x in range(1, 11) if x % 2 == 0])    # 筛选出仅偶数的平方
# 结果:[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 结果:[4, 16, 36, 64, 100]
print([m + n for m in 'ABC' for n in 'XYZ'])    # 两层循环,生成全排列
# 结果:['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']

d = {'x': 'A', 'y': 'B', 'z': 'C' }
print([k + '=' + v for k, v in d.items()])    # for循环同时使用两个变量
# 结果:['y=B', 'x=A', 'z=C']
# 通过一个复杂的列表生成式把 dict 变成一个 HTML 表格
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 }
tds = ['<tr><td>%s</td><td>%s</td></tr>' % (name, score) for name, score in d.items()]
print( '<table>' )
print( '<tr><th>Name</th><th>Score</th><tr>' )
print( '\n'.join(tds) )
print( '</table>' )
# 字符串的join()方法可以把一个 list 拼接成一个字符串
# 结果:
<table>
<tr><th>Name</th><th>Score</th><tr>
<tr><td>Bart</td><td>59</td></tr>
<tr><td>Lisa</td><td>85</td></tr>
<tr><td>Adam</td><td>95</td></tr>
</table>
# 把打印出来的结果保存为一个html文件,就可以在浏览器中看到效果
# lower() 函数的参数类型只能是 str ,用列表生成器修改列表 L1 ,保证lower() 正确调用
L1 = ['Hello', 'World', 18, 'Apple', None]
L2 = [x.lower() for x in L1 if isinstance (x, str)]
print(L2)
# 结果:['hello', 'world', 'apple']

9.4 生成器 generator

   列表容量有限,而且占用很大的存储空间。通过生成器,列表元素可以按照某种算法推算出来,可以在循环的过程中不断推算出后续的元素。

9.4.1 把列表生成式改成 generator

L = [x * x for x in range(10)]    # 列表生成器
g = (x * x for x in range(10))    # 生成器 把[]改成()
print(g)
# 结果:<generator object <genexpr> at 0x1022ef630>

  打印出 generator 的每个元素有两种方法:一种是通过 next() 获得;二是通过 for 循环 (一般用此方法)。

# 通过 next() 打印出 generator 的每个元素
g = (x * x for x in range(3))
print(next(g))
print(next(g))
print(next(g))
print(next(g))  # 没有更多的元素时,抛出StopIteration的错误 
# 结果:
0
1
4
Tracebacke (most recent call last):
  File xxxxx,line x , in xx
StopIteration
# 通过 for 循环来迭代
g = (x * x for x in range(3))
for n in g:            # 不会产生 StopIteration 的错误
    print(n)  
# 结果:
0
1
4

9.4.2 通过函数实现复杂逻辑的 generator

  如果推算的算法比较复杂,用类似列表生成式的 for 循环无法实现的时候,还可以用函数来实现。

  65. generator 的函数,在每次调用 next() 的时候执行,遇到 yield 语句返回,再次执行时从上次返回的 yield 语句处继续执行。

# 用函数生成斐波拉契数列(除第一个和第二个数外,任意一个数都可由前两个数相加得到)1, 1, 2, 3, 5, 8, 13, 21, 34, ...

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b                              # 把函数中的 return 改为 yield,函数就变成 generator   
        a, b = b, a + b
        n = n + 1
    return 'done'

f = fib(6)
print(f)
# 结果 :<generator object fib at 0x000001F4575E30A0>

for n in f:
    print(n)
# 结果 :
1
1
2
3
5
8
  
赋值语句 a, b = b, a + b
相当于 t = ( b, a+b)   # t 是一个 tuple
    a = t[0]
           b = t[1]     # tuple 中 a+b 在父子之前就计算好了
a, b = b, a + b 的解释
# 用函数生成杨辉三角
def triangles():
    L = [1]
    while True:
        yield L
        L.append(0)
        L = [L[i-1] + L[i] for i in range(len(L))]

n
= 0 for t in triangles(): print(t) n = n + 1 if n == 10: break # 如果没有 break ,将无限循环
# 结果:
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
首先杨辉三角和之前的汉诺塔一样,也要找到规律。

第N行有N个数,那么第N行就比第N-1行多出1个数,每次加上一个0可以实现这个规律,至于为什么加0,等下再说。

除了上面说的废话之外,要得出每一行具体是什么数还需要找到另外的规律,这里不能按照“肩上的两个数之和”的规律来得出,只能在本行内找规律。

另外L[i-1] + L[i]是什么意思呢?也就是前一个数加上后一个数。

下面来看实际的过程。

一开始L = [1],只有一个数,第一轮直接返回[1],第二轮开始每次都要加上一个0则:

第二轮开始 L = [1, 0] 此时列表内有2个数,i的取值范围为[0, 1],带入L[i-1] + L[i],则得到[0+1, 1+0]。实际上这是“倒数第一个数和正数第一个数的和”以及“正数第一个数和正数第二数的和”。最后得到 L = [1, 1]

第三轮开始 L = [1,1,0] 此时列表内有3个数,i的取值范围为[0, 1 ,2],带入L[i-1] + L[i],则得到[0+1, 1+1, 1+0]。实际上这是“倒数第一个数和正数第一个数的和”、“正数第一个数和正数第二数的和”以及“正数第二个数和正数第三个数的和”。最后得到L = [1, 2, 1]。

第三轮开始 L = [1, 2, 1, 0] 此时列表内有4个数,i的取值范围为[0, 1, 3],带入L[i-1] + L[i],则得到[0+1, 1+2, 2+1, 1+0]。实际上这是“倒一和正一的和”、“正一和正二的和”、“正二和正三的和”以及“正三和正四的和”。最后得到L = [1, 3, 3, 1]。

到这里规律就出来了,每次加上一个0之后,每行之间的数也就是从倒数第一个数开始每次加上前一个数的和。至于为什么加上0,因为杨辉三角形每行最后是1,而1 = 1 + 0,加上0才能使最后一个数永远是1。

之前就是不明白L[i-1] + L[i]以及为什么加0的问题,想了半天手写了一下找了一下规律茅塞顿开
杨辉三角的算法解释(来源:半分幻的凶鳥)

9.5 迭代器 Iterator

  可以用 for 循环的对象统称为可迭代对象 Iterable 。包括:list tuple dict set str / generator [isinstance(xxx,Iterable)]

  可以被 next() 函数调用并不断返回下一个值的对象称为迭代器 Iterator 。生成器都是 Iterator 对象 [isinstance(xxx,Iterator)]

  Python 的 Iterator 对象表示的是一个数据流,Iterator 对象可以被 next() 函数调用并不断返回下一个数据,直到没有数据时抛出 StopIteration 错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过 next() 函数实现按需计算下一个数据,所以 Iterator 的计算是惰性的,只有在需要返回下一个数据时它才会计算。Iterator 甚至可以表示一个无限大的数据流,例如全体自然数。而使用 list 是永远不可能存储全体自然数的。

  使用 iter() 函数可以把 list tuple dict set str 等 Iterable 变成 Iterator。

from collections import Iterator
print(isinstance([], Iterator))     # 结果:False
print(isinstance({}, Iterator))     # 结果:False
print(isinstance('', Iterator))     # 结果:False
print(isinstance(iter([]), Iterator))     # 结果:True
print(isinstance(iter({}), Iterator))   # 结果:True
print(isinstance(iter(''), Iterator))     # 结果:True

 


  小结:

  文中对 list 、tuple、dict、set 的区别与联系写的不够清晰,在这里再总结一下

  有/无序 可切片 可迭代 可列表生成 len() 删除 添加 更改
list 可[enumerate()] .pop(索引)/.remove(元素)/del [索引] .append(元素)/.insert(索引,元素)/.extend([]) [索引]=
tuple
dict 不可 可[key/.values()/.items()] .pop(key)/del [key] [key]=/.update({}) [key]=
set 不可 .remove(元素)/.discard(元素) .add(元素)

转载注明地址:http://www.chengxuyuans.com/Python/104700.html

推荐文章