Python基础学习

本文介绍了Python中的序列类型,如列表、元组和字符串,以及如何进行序列解包、比较和操作。详细阐述了字典的使用,包括迭代、更新和删除元素。此外,还讲解了循环语句,如while和for,以及条件语句,如if-else。特别强调了函数的定义、参数传递、作用域和递归。最后,探讨了函数式编程的概念,如map、filter和reduce。
摘要由CSDN通过智能技术生成

先睹为快

  1. 获取用户输入

    input("The meaning of life: ")
    
    The meaning of life: 42
    
    '42'
    

    输入的’42’被input(以文本或字符串的方式)返回

  2. if语句的使用

    if 1 == 2: print('One equals two')
    elif 1 == 1 print('One equals One')
    
  3. 函数

    print(pow(2,3))
    print(abs(-10))#计绝对值
    print(round(2/3))#圆整到最接近的整数,并在两数一样接近的时候圆整到偶数
    #floor函数向下取整,当然需要头文件
    
  4. 模块

    视为拓展,将其导入可扩展Python的功能。可使用的特殊命令import

    import math
    print(math.floor(32.9))
    print(int(32.9))
    print(math.ceil(32.3)) #与floor相反,向上取整
    
    # 也可以用如下方式导入模块,省的每次调用函数都指定模块
    from math import sqrt
    print(sqrt(9))
    #提示:事实上,可使用变量来引用函数(以及其他大部分Python元素)。执行赋值语句foo = math.sqrt后,就可以使用foo来计算平方根。例如,foo(4)的结果是2.0。
    
  5. cmath 和复数

    import cmath
    print(cmath.math(-1))
    #结果为1J 1J是个虚数,虚数都是以j(J)结尾
    
  6. 字符串的输入输出

    name = input('What's your name ?)
    print('Hello, ' + name + '!')
    
  7. 三角形绘制图法

    from turtle import *
    forward(100)
    left(120)
    forward(100)
    left(120)
    forward(100)
    

列表和元组

列表和元组的不同点,列表是可以修改的,而元组不可以。通常情况下,列表是可以用来代替元组的,但在元组用作字典键的时候,不能用列表代替元组,因为字典键是不允许修改的。

Python支持一种数据结构的基本概念,名为容器。其中两个主要的容器是序列(如列表和元组)和映射(如字典)。在序列中每个元素都有编号,而在映射中,每个元素都有名称(也叫键)。而集合(set)既不是序列也不是映射的容器。

序列

索引、切片、相加、相乘和成员资格检查。

索引
greeting = 'Hello'
print(greeting[0])

可以使用索引来获取元素,当负数为索引时,Python将从右往左数(即从最后一个元素)。

print(greeting[-1])

如果函数调用返回一个序列,可直接对其执行索引操作如:

fourth = input('Year:')[3]
print(fourth)
切片

除了使用索引来访问单个元素外,还可以使用切片来访问特定范围内的元素。

tag = '<a href="http://www.python.org">Python web site</a>' 
print(tag[9:30])
print(tag[32:-4])

如上述,其中第一个索引指定的元素包含在切片内,但第二个索引指定的元素不包含在切片内。

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
numbers[-3:0]

上述代码运行结果为空,事实上,在执行切片操作时,如果第一个索引指定的元素位于第二个索引指定的元 素后面,结果就为空序列。因此可以使用一种简写:如果切片结束与序列末尾,可省略第二个索引。

   numbers[-3:]

同样,如果切片始于序列开头,可省略第一个索引。

numbers[:3]

如果要复制整个序列,可将两个索引都省略。

numbers[:]

显式的指定步长来提取元素。

numbers[3:6:3] # 步长为3,将从起点到终点之间,每隔两个元素提取一个元素。
numbers[::4] # 步长为4,将从起点到终点之间,每隔三个元素提取一个元素

当然步长不能为0,否则无法向前移动,但可以为负数,即从右向左提取元素。

numbers[8:3:-1] # [9,8,7,6,5]
numbers[0:10:-2] #[]
numbers[::-2] # [10,8,6,4,2]
numbers[5::-2] # [6,4,2]
numbers[:5:-2] # [10,8]
序列相加

可使用加法运算符来拼接序列。

[1, 2, 3] + [4, 5, 6] # [1,2,3,4,5,6]
'Hello,' + 'world!' # [Hello,world!]

但不能拼接列表和字符串,虽然他们都是序列,一般而言,不能拼接不同类型的序列。

乘法

将序列和数X相乘,将重复这个序列X次来创建一个新序列:

[8]*5 # [8,8,8,8,8]

None、空列表和初始化

空列表是使用不包含任何内容的两个方括号表示的,如果想创建一个包含10个元素的列表,但没有任何内容,可用如下方法:

[0]*10

然而在有些情况下,你可能想表示“什么都没有”的值,如表示在列表还没有添加任何内容。这种情况下,可使用None,None表示什么都没有,因此想将列表的长度初始化为10,可像下面这样做:

sequence = [None] * 10 # [None, None, None, None, None, None, None, None, None, None]
成员资格

要检查特定的值是否包含在序列中,可使用运算符in,其检查是否满足指定的条件,并返回相应的值:True Or False。这样的运算符成为布尔运算符,而前述真值为布尔值。

permissions = 'student'
print('t' in permissions)

users = ['mlh', 'foo', 'bar']
input('Enter your user name:') in users
# Enter your user name: mlh
# True
# 检查用户名和PIN码
database = [
    ['albert' , '1234'],
    ['dilbert', '4242'],
    ['smith', '7524'],
    ['jones', '9843']
]
username = input('User name: ')
pin = input('Pin Code: ')
if [username, pin] in database:
    print('Access granted')
    
'''
User name: jones
Pin Code: 9843
Access granted
'''

长度、最小值和最大值

其中内置函数len返回序列包含的元素个数,而min和max分别返回序列中最小和最大的元素。

numbers = [100, 34, 678]
print(len(numbers))
print(max(numbers))
print(min(numbers))

'''
3
678
34
'''
列表 :Python的主力

下面主要讨论列表不同于元组和字符串的地方——列表是可变的,即可修改其内容。

函数list
list('Hello') # ['H', 'e', 'l', 'l', 'o']

可将任何序列(而不仅仅是字符串)作为list的参数。

基本的列表操作
修改列表:给元素赋值
x = [1,1,1]
x[1] = 2
print(x) 
# [1,2,1]
删除元素
names = ['Alice','Beth','Cecil','Dee-Dee','Earl']
del names[2]
print(names)
# ['Alice', 'Beth', 'Dee-Dee', 'Earl']

del语句还可用于删除其他东西,可将用于字典乃至变量。

给切片赋值
name = list('Perl')
print(name)
name[2:] = list('ar')
print(name)
# ['P', 'e', 'r', 'l']
# ['P', 'e', 'a', 'r']

通过切片赋值,可将切片替换为长度与其不同的序列。

name = list('Perl')
name[1:] = list('ython')
print(name)
# ['P', 'y', 't', 'h', 'o', 'n']

可通过“替换”一个空切片,相当于插入一个序列,以此措施来删除切片。

numbers = [1,2,3,4,5]
numbers[1:4]
print(numbers)
# [1, 5]
列表方法

方法是与对象(列表、数、字符串等)联系紧密的函数。通常,像下面这样的调用方法:

object.method(arguments)
  1. append

    方法append用于将一个对象附加到列表末尾。

    lst = [1,2,3]
    lst.append(4)
    print(lst)
    # [1, 2, 3, 4]
    

    append是就地修改列表,不会返回新列表。

  2. clear

    方法clear就地清空列表的内容

    lst = [1,2,3]
    lst.clear()
    print(lst)
    

    类似于切片赋值语句lst[:] = []

  3. copy

    方法copy复制列表。前面说过,常规复制只是将另一个名称关联到列表。

    a = [1,2,3]
    b = a
    b[1] = 4
    print(a)
    # [1, 4, 3]
    

    要让a和b指向不同的列表,就必须将b关联到a的副本。

    a = [1,2,3]
    b = a.copy()
    b[1] = 4
    print(a)
    # [1,2,3]
    
  4. count

    方法count计算指定的元素在列表中出现了多少次。

    print(['to','be','or','not','to','be'].count('to'))
    x = [[1,2],1,1,[2,1,[1,2]]]
    print(x.count(1)) # 2
    print(x.count([1,2])) # 1
    
  5. extend

    方法extend将多个值附加到列表末尾,为此可将这些值组成的序列作为参数提供给方法extend,简单的来说,是使用一个列表来扩展另一个列表。

    a = [1,2,3]
    b = [4,5,6]
    a.extend(b)
    print(a) 
    # [1,2,3,4,5,6]
    

    使用extend扩展得到的列表与拼接出来的列表完全相同,但拼接的效率比extend的效率低。

    要获得与extend相同的效果,可将列表赋给切片:

    a = [1,2,3]
    b = [4,5,6]
    a[len(a):] = b
    print(a)
    # [1,2,3,4,5,6]
    

    虽然可行,但可读性不高。

  6. index

    方法index在列表中查找指定值第一次出现的索引。

    knights = ['We','are','the','knights','who','say','ni']
    knights.index('who')
    
  7. insert

    方法insert用于将一个对象插入到列表。

    numbers = [1,2,3,4,5,6,7]
    numbers.insert(3,'four')
    print(numbers)
    # [1, 2, 3, 'four', 4, 5, 6, 7]
    

    与extend一样,也可使用切片赋值来获得与insert一样的效果。

    numbers = [1,2,3,4,5,6,7]
    numbers[3:3] = ['four']
    print(numbers)
    # [1, 2, 3, 'four', 4, 5, 6, 7]
    
  8. pop

    方法pop从列表中删除一个元素(末尾为最后一个元素),并返回这一个元素。

    x = [1,2,3]
    print(x.pop())
    # 3
    x = [1,2,3,4]
    print(x.pop(1))
    print(x)
    # 2
    # [1,3,4]
    

    注意 pop是唯一既修改列表又返回一个非None值的列表方法。

    使用pop可实现一种常见的数据结构——栈(stack),后进先出。

    push和pop是普遍的两种栈操作(加入和取走)的名称。Python没有提供push,但可用append来替代。方法pop和append的效果相反。

    x = [1,2,3]
    x.append(x.pop)
    print(x)
    # [1,2,3]
    

    提示:要创建先进先出(FIFO)的队列,可使用insert(0,…) 代替append。另外可继续使用append,但用pop(0) 替代pop()。

  9. remove

    方法remove用于删除第一个为指定值的元素。

    x = ['to','be','or','not','to','be']
    x.remove('be')
    prnit(x)
    # ['to','or','not','to','be']
    

    remove是就地修改且不返回值的方法之一。

  10. reverse

    方法reverse按相反的顺序排列列表中的元素

    x = [1,2,3]
    x.reverse()
    prnit(x)
    # [3,2,1]
    

    reverse同样修改表,但不返回任何值。

  11. sort

    方法sort用于对列表就地排序。就地排序意味着对原来的列表进行修改,使其元素按顺序排序,而不是返回排序后的列表副本。

    x = [3,4,6,2,1,0]
    x.sort()
    print(x)
    
    x = [3,4,6,2,1,0]
    y = x.copy()
    y.sort()
    print(y)
    print(x)
    # [0, 1, 2, 3, 4, 6]
    # [3, 4, 6, 2, 1, 0]
    

    只是将x赋给y是不行的,因为这样x和y将指向同一个列表。为获取排序后的列表副本,另一种方式是使用函数sorted。

    x = [3,4,6,2,1,0]
    y = sorted(x)
    print(x)
    print(y)
    # [3, 4, 6, 2, 1, 0]
    # [0, 1, 2, 3, 4, 6]
    
  12. 高级排序

    方法sort接受两个可选参数:key和reverse。这两个参数通常是按名称指定的,成为关键字。

    x = ['aardvark','abalone','acme','add','aerate']
    x.sort(key=len)
    print(x)
    # ['add', 'acme', 'aerate', 'abalone', 'aardvark']
    
    x = [4,6,2,1,7,9]
    x.sort(reverse=True)
    print(x)
    # [9, 7, 6, 4, 2, 1]
    
元组:不可修改的序列

与列表一样,元组也是序列,唯一的差别在于元组是不可修改的。元组语法很简单,只要将一些值用逗号分隔,就能自动创建一个元组。

(1,2,3)
(1,) # 表示一个值的元组
3*(40+2) # 126
3*(40+2,) # (42,42,42)

函数tuple的工作原理与list很像:它将一个序列作为参数,并将其转换为元组。如果参数已经是元组,就原封不动地返回它。

tuple([1,2,3]) # (1,2,3)
tuple('abc') #('a','b','c')
tuple((1,2,3)) # (1,2,3)

使用字符串

前面说过,所有标准序列操作都适用于字符串,但字符串是不可变的,因此所有的元素赋值和切片赋值都是非法的。

设置字符串的格式 :精简版

指定设置其格式的值时,可使用单个值(如字符串和数字),可使用元组(如果要设置多个值的格式),还可使用字典,其中最常见的是元组。

format = "Hello, %s. %s enough for ya?"
values = ('world', 'Hot')
print(format % values) # Hello, world. Hot enough for ya?

上述格式字符串中的%s称为转换说明符。s意味着将值视为字符串进行格式设置。如果指定的值不是字符串,将使用str将其转换为字符串。其他说明符亦如此,例如,%.3f将值的格式设置为包含3位小数的浮点数。

print("{},{} and {}".format("first","second","third"))
print("{0},{1},{2}".format("first","second","third"))
print("{3} {0} {2} {2} {1} {3} {0}".format("be","not","or","to"))

# first,second and third
# first,second,third
# to be or or not to be

from math import pi
print("{name} is approximately {value:.2f}.".format(value=pi, name="Π"))
# Π is approximately 3.14.

# 如果变量与替换字段同名,还可使用一种简写。在这种情况下,可使用f字符串——在字符串前面加上f。
from math import e
print(f"Euler's constant is roughly {e}.")
# Euler's constant is roughly 2.718281828459045.

# 在这里,创建最终字符串时,将把替换字段e替换为变量e的值。这与下面这个更明确一些的表达式等价:
print(f"Euler's constant is roughly {e}.".format(e=e))
字符串方法
center

方法center通过在两边填充字符(默认为空格)让字符居中。

print("The Middle by Jimmy Eat World".center(39))
print("The Middle by Jimmy Eat World".center(39,"*"))
#      The Middle by Jimmy Eat World
# *****The Middle by Jimmy Eat World*****
find

方法find在字符串中查找子串。如果找到,就返回字串的第一个字符的索引,否则返回-1。

print("The Middle by Jimmy Eat World".find('by'))
# 11
title = "The Middle by Jimmy Eat World"
print(title.find('by'))

还可以指定搜索的起点和终点。

subject = "by Get by rich by now !!! by"
print(subject.find('by',8))
# 15 返回字符串第一个字串的位置
join

join是一个非常重要的字符串方法,其作用与split相反,用于合并序列的元素。

seq = [1,2,3,4,5]
sep = '+'
sep.join(seq) # sequence item 0: expected str instance, int found
seq = ['1','2','3','4','5']
print(sep.join(seq))
dirs = '','usr','bin','env'
print('/'.join(dirs))
print('C:'+'\\'.join(dirs))
# 1+2+3+4+5
# /usr/bin/env
# C:\usr\bin\env

如上述,所合并序列的元素必须是字符串。

lower

方法lower返回字符串的小写版本。

name = 'Gumby'
names = ['gumby','smith','jones']
if name.lower() in names: print('Found it')
# Found it

另一个与lower相关的方法是title。它将字符串转为词首大写,即所有单词的首字母都大写,其他字母小写。

print("that's all folks".title())

import string
print(string.capwords("that's all, folks"))

# That'S All Folks
# That's All, Folks
split

split 其作用与join相反,用于将字符串拆分为序列。

print('1+2+3+4+5'.split('+'))
# ['1', '2', '3', '4', '5']

注意,如果没有指定分隔符,将默认在单个或多个连续的空白字符(空格、制表符、换行符等)处进行拆分。

strip

方法strip将字符串开头和末尾的空白(但不包括中间的空白)删除,并返回删除后的结果。

print('     The Middle by Jimmy Eat World      '.strip())
# The Middle by Jimmy Eat World
print('******The Middle * by ** Jimmy ** Eat World****!!!!'.strip('!*'))
# The Middle * by ** Jimmy ** Eat World
translate

方法translate与replace一样替换字符串的特定部分,但不同的是它只能进行单字符替换。这个方法的优势在于能够同时替换多个字符。

使用translate前必须创建一个转换表。这个转换表指出了不同Unicode码点之间的转换关系。要创建转换表,可对字符串类型的str调用方法maketrans,这个方法接受两个参数:两个相同长度的字符串,前一个替换为后一个。

table = str.maketrans('cs','kz')
print('this is an incredible test'.translate(table))
# thiz iz an inkredible tezt
# maketrans提供可选的第三个参数,指定要将哪些字母删除。
table = str.maketrans('cs','kz',' ')
print('this is an incredible test'.translate(table))
# thizizaninkredibletezt

字典

字典是Python中唯一的内置映射类型,其中的值不按顺序排列,而是存储在键下。键可能是数、字符串或元组。

创建和使用字典

字典由键及相应的值组成,这种键-值对称为项(item)。在前面的示例中,键为名字,而值为电话号码。每个键与其值之间都用冒号分隔,项之间用逗号分隔,而整个字典放在括号内。空字典用两个花括号表示,类似于{}。

phonebook = {'Alice': '2341','Beth': '9102','Cecil':'3258'}
函数dict

可使用函数dict从其他映射(如其他字典)或键-值对序列创建字典。

注意 dict与list、tuple和str一样,dict其实根本就不是函数,而是一个类。

items = [('name','Gumby'),('age',42)]
d = dict(items)
print(d)
print(d['name'])
# {'name': 'Gumby', 'age': 42}
# Gumby
# 还可以使用关键字实参来调用这个函数
d = dict(name = 'Gumby', age = 42)
print(d)
# {'name': 'Gumby', 'age': 42}
基本的字典操作

len(d) 返回字典d包含的项(键-值对)数。

d[k] 返回与键k相关联的值

d[k] = v 将值v关联到键k

del d[k] 删除键为k的项

k in d检查字典d是否包含键为k的项

  • 键的类型:字典中的键可以是任何不变的类型,如整数,浮点数(实数)、字符串或元组。
  • 自动添加:使用append或其他类似的方法,就可以在字典原本没有键的情况下,也可以给它赋值,即在字典创建一个新项。
  • 成员资格:表达式 k in d(其中d是一个字典)查找的是键而不是值,而表达式v in l(其中 l 是一个列表)查找的是值而不是索引。
x = []
x[42] = 'Foobar' # IndexError: list assignment index out of range
# 上述初始化x时,必须使用[None] * 43之类的代码,而不能使用[]
x = {}
x[42] = 'Foobar'
print(x) # {42: 'Foobar'}
# 这次将'Foobar'赋给一个空字典的键42,在该字典添加了一个新项
# 一个简单的数据库
# 一个将人名用作键的字典。每个人都用一个字典表示
# 字典包含键'phone'和'addr',它们分别与电话号码和地址相关联
people = {
    'Alice' : {
        'phone' : '2341',
        'addr' : 'Foo drive 23'
    },
    'Beth' : {
        'phone' : '9102',
        'addr' : 'Bar street 42'
    },
    'Ceil' : {
        'phone' : '3158',
        'addr' : 'Baz avenue 90'
    }
}
# 电话号码和地址描述标签,共打印输出时使用
labels = {
    'phone' : 'phone number',
    'addr' : 'address'
}

name = input('Name: ')

# 要查找电话号码还是地址?
request = input('Phone number (p) or address (a)?')

# 使用正确的键
if request == 'p' : key = 'phone'
if request == 'a' : key = 'addr'

# 仅当名字时字典包含的键时才打印信息:
if name in people : print("{}'s {} is {}.".format(name, labels[key], people[name][key]))

# Name: Beth
# Phone number (p) or address (a)?p
# Beth's phone number is 9102.
将字符串格式设置功能用于字典
phonebook = {'Alice': '2341','Beth': '9102','Cecil':'3258'}
print("Ceil's phone number is {Cecil}.".format_map(phonebook))
# Ceil's phone number is 3258.
字典方法
clear

方法clear删除所有的字典项(返回None)。

d.clear()
x = {}
y = x
x['key'] = 'value'
print(y) 
x = {}
print(y)
# {'key': 'value'}
# {'key': 'value'}
# 上述通过x = {},想通过将一个空字典赋给x来“清空”它,这对于y没有任何影响,它依然指向原来的字典。
# 若要删除原来字典的所有元素,必须使用clear,这样的话,y将也是空的
x.clear()
print(y) # {}
copy

方法copy返回一个新字典,其中包含的键-值与原来的字典相同(这个方法是浅复制,因为值本身是原件,而非副本)。

x = {'username' : 'admin', 'machines' : ['foo','bar','baz']}
y = x.copy()
y['username'] = 'mlh'
y['machines'].remove('bar')
print(y)
print(x)
# {'username': 'mlh', 'machines': ['foo', 'baz']}
# {'username': 'admin', 'machines': ['foo', 'baz']}
# 当替换副本中的值时,原件不受影响。然而,如果修改副本中的值(就地修改而不是替换),原件也将发生变换,因为原件指向的也是被修改的值。

# 为避免这种问题,一种办法是执行深复制,即同时复制值及其包含所有值。
from copy import deepcopy
d = {}
d['names'] = ['Alfred', 'Bertrand']
c = d.copy()
dc = deepcopy(d)
d['names'].append('Clive')
print(c)
print(dc)
#{'names': ['Alfred', 'Bertrand', 'Clive']}
#{'names': ['Alfred', 'Bertrand']}
fromkeys

方法fromkeys创建一个新字典,其中包含指定的键,且每个键对应的值都是None。

print({}.fromkeys(['name','age'])) 
# {'name': None, 'age': None}

# 这个示例首先创建一个空字典,再对其调用方法fromkeys来创建另一个字典,这有点多余
# 可以直接对dict(dict是所有字典所属的类型)调用方法fromkeys

print(dict.fromkeys(['name','age']))
# {'name': None, 'age': None}
# 若不想使用默认值None,可提供特定的值。
print(dict.fromkeys(['name','age'],'(unknown)'))
# {'name': '(unknown)', 'age': '(unknown)'}
get

方法get为访问字典项提供了宽松的环境。通常,若试图访问字典中没有的项,将引发错误。

d = {}
print(d['name'])
'''
Traceback (most recent call last):
  File "c:\Users\23850\Desktop\Python\Day01.py", line 275, in <module>
    print(d['name'])
KeyError: 'name'
'''

而使用get就不会出现这样

print(d.get('name')) # None

# 同样你可以指定“默认”值
print(d.get('name','N/A')) # N/A

items

方法items返回一个包含所有字典项的列表,其中每个元素都为(key, value)的形式。字典项在列表中的排列顺序不确定。

d = {'title':'Python Web Site','url':'http://www.python.org','spam':0}
print(d.items())
# dict_items([('title', 'Python Web Site'), ('url', 'http://www.python.org'), ('spam', 0)])

其返回值属于一名为字典视图的特殊类型。字典视图可用于迭代。可确定其长度以及执行成员资格检查。

it = d.items()
len(it)
('Spam',0) in it

如下可将字典项复制到列表中。

list(d.items())

keys

方法keys返回一个字典视图,其中包含指定字典中的键。

print(d.keys())
# dict_keys(['title', 'url', 'spam'])

popitem

方法popitem类似于list.pop,但list.pop弹出列表中的最后一个元素,而popitem随机地弹出一个字典项,因为字典项的顺序是不确定的。

setdefault

方法setdefault有点像get,因为它也获取与指点键相关联的值,但除此之外,setdefault还在字典不包含指定的键是,在字典中添加指定的键-值对。

d = {}
d.setdefault('name','N/A')
print(d)
# {'name': 'N/A'}
d['name'] = 'Gumby'
d.setdefault('name','N/A')
print(d)
# {'name': 'Gumby'}
update

方法update使用一个字典中的项来更新另一个字典。

d = {
    'title' : 'Python Web Site',
    'url' : 'http://www.python.org',
    'changed' : 'Mar 14 22:09:15 MET 2016'
}
x = {'title' : 'Python Language Website'}
d.update(x)
print(d)

# {'title': 'Python Language Website', 'url': 'http://www.python.org', 'changed': 'Mar 14 22:09:15 MET 2016'}
values

方法values返回一个由字典中的值组成的字典视图。不同于方法keys,方法values返回的视图可能包含重复的值。

d = {}
d[1] = 1
d[2] = 2
d[3] = 3
d[4] = 1
print(d.values())

# dict_values([1, 2, 3, 1])

条件、循环及其他语句

再谈print和import
打印多个参数
print('Age:', 42) 

# Age: 42

name = 'Gumpy'
salutation = 'Mr.'
greeting = 'Hello,'
print(greeting, salution, name)

# Hello, Mr. Gumpy

print(greeting + ',', salution, name)

# 如果需要可自定义分隔符
print("I", "wish", "to", "register", "a", "complaint", seq = "_")

# I_wish_to_register_a_complaint

# 可自定义结束字符串,以替换默认的换行符。例如,如果将结束字符串指定为空字符串,以后就可继续打印到当前行。
print("Hello,", end = "")
print("world!")
# Hello,world!

导入时重命名
# 从模块导入时,通常用
import someodule
# 或使用
from someodule import somefunction
# 或
from someodule import *

# 在语句末添加as子句并指定别名
import math as foobar
foobar.sqrt(4)
# 下面是一个导入特定函数并给它指定别名的列子:
from math import sqrt as foobar
foobar(4)

赋值魔法
序列解包
x, y, z = 1, 2, 3
print(x, y, z)
# 1, 2, 3

x, y = y, x
print(x, y, z)
# 2, 1, 3

实际上,这里执行的操作称为序列解包(或可迭代对象解包):将一个序列(或任何可迭代对象)解包,并将得到的值存储到一系列变量中。如下解释。

values = 1, 2, 3
print(values) 
# (1, 2, 3)
x, y, z = values
print(x, y, z)
# 1 2 3

这在返回元组(或其他序列或可迭代对象)的函数或方法时很有用。假设要从字典随便获取(或删除)一个键-值对,可使用方法popitem,以便获取一个键-值对并以元组的方式返回。接下来,可直接将返回的元组解包到两个变量中。

scoundrel = {'name': 'Robin', 'girlfriend': 'Marion'}
key, value = scoundrel.popitem()
print(key, value)
# girlfriend Marion

要解包的序列包含的元素个数必须与你在等号左边列出的目标个数相同,否则异常。

可使用星号符(*)来收集多余的值,这样无需确保值和变量的个数相同。

a, b, *rest = [1, 2, 3, 4]
print(rest)
# [3, 4]
# 其中可将带星号的变量放在其他位置。

赋值语句的右边可以是任何类型的序列,但带星号的变量最终包含的总是一个列表。在变量和值的个数相同时亦是如此。

a, *b, c = "abc"
print(a, b, c)
# a ['b'] c

链式赋值

链式赋值是一种快捷方式,用于将多个变量关联到同一个值。

x = y = somefunction()
# 上述代码与下等价
y = somefunction()
x = y
# 注意,这两条语句可能与下面的语句不等价
x = somefunction()
y = somefunction()

增强赋值
x += 2
x *= 3
# 适用于所有标准运算符,如*、/、%等,亦可用于其他数据类型(只要使用的双目运算符可用于这些数据类型)。
fnord = 'foo'
fnord += 'bar'
fnord *= '2'

条件和条件语句

用作布尔表达式(如用作if语句中的条件)时,下面的值都将被解释器视为假:

False None 0 “” () [] {}

其他值都被视为真,包括特殊值True。

print(True) # True
print(False) # False
print(True == 1) # True
print(False == 0)# True
print(True + False + 42) # 43
print(bool('I think, therefore I am')) # True
print(bool(42)) # True
print('') # False
print(0) # False

# 虽然bool([]) == bool("") == False,但他们并不相等(即[] != "")

有条件地执行和if语句
# Python endswith() 方法用于判断字符串是否以指定后缀结尾,如果以指定后缀结尾返回True,否则返回False。
# str.endswith(suffix[, start[, end]])
# suffix -- 该参数可以是一个字符串或者是一个元素。
# start -- 字符串中的开始位置。
# end -- 字符中结束位置。

name = input('What is your name?')
if name.endswith('Gumby'):
    print('Hello, Mr. Gumby')
# Hello, Mr. Gumby

else 子句
name = input('What is your name?')
if name.endswith('Gumby'):
    print('Hello, Mr. Gumby')
else:
    print('Hello, stranger')

elif 子句
num = int(input('Enter a number: '))
if num > 0:
    print('The number is positive')
elif num < 0:
    print('The number is nagative')
else:
    print('The number is zero')

代码嵌套

即可将if语句放在其他if语句中。

更复杂的条件
比较运算符
表达式描述
x == y
x < y
x > y
x >= y
x <= y
x != y
x is yx和y同一对象
x is not yx和y是不同的对象
x in yx是容器(如序列)y的成员
x not in yx不是容器(如序列)y的成员

与赋值一样,Python也支持链式比较:可同时使用多个比较运算比较符,如0 < age < 100。

  • is:相同运算符

    这个运算符看似与==一样,但实际并非如此。

    x = y = [1, 2, 3]
    z = [1, 2, 3]
    print(x == y) # True
    print(x == z) # True
    print(x is y) # True
    print(x is z) # False
    
    x = [1, 2, 3]
    y = [2, 4]
    print(x is not y) # True
    del x[2]
    y[1] = 1
    y.reverse()
    
    print(x == y) # True
    print(x is y) # False
    

    总之,==用来检查两个对象是否相等,而is用来检查两个对象是否相同(是同一个对象)。

    警告:不要将is用于数和字符串等不可变的基本值,这样做的结果是不可预测的。

  • in:成员资格运算符

    name = input('What is your name?')
    if 's' in name:
        print('Your name contains the letter "s".')
    else:
        print('Your name does not contain the letter "s".')
    
  • 字符串和序列的比较

    字符串是根据字符的字母排列顺序进行比较的。

    print("alpha"<"beta") # True
    

    实际上,字符是根据顺序值排序的。要获悉字母的顺序值,可使用函数ord。这个函数的作用与函数chr相反。

    print(ord("🥧"))
    print(ord("💕"))
    print(chr(128149))
    
    # 129383
    # 128149
    # 💕
    
    # 忽略大小写进行排序
    print("a".lower()<"B".lower())
    
    # 其他序列的比较方式与此相同,但这些序列包含的元素可能不是字符,而是其他类型的值。
    print([1, 2]<[2, 1])
    # True
    print([2, [1, 4]] < [2, [1, 5]])
    # True
    
    
布尔运算符
number = int(input('Enter a number between 1 and 10: '))
if number <= 10 and number >= 1:
    print('Great!')
else:
    print('Wrong!')
# Enter a number between 1 and 10: 7
# Great!

# 另外还有两个布尔运算符:or 和 not
if((cash > price) or customer_has_good_credit) and not out_of_stock:
    give_goods()

断言

其工作原理类似于下面的伪代码:

if not condition:
    crash program

让程序在错误条件出现时立即崩溃胜过以后再崩溃(可在核实函数参数满足要求或为初始测试和调试提供帮助)。

为此可在语句中使用关键字assert。

如果知道必须满足特定条件,程序才能运行,可在程序中添加assert语句充当检查点。

age = -1
assert 0 < age < 100, 'The age must be realistic'

# 可在条件后面添加一个字符串,对断言进行说明
# Traceback (most recent call last):
#   File "c:\Users\23850\Desktop\Python\Day01.py", line 370, in <module>
#     assert 0 < age < 100, 'The age must be realistic'
# AssertionError: The age must be realistic

循环
while 循环
x = 1
while x <= 100:
    print(x)
    x += 1

# 可使用循环来确保用户输入名字
name = ''
while not name:
    name = input('Please enter your name: ')
print('Hello, {}!'.format(name))
# Please enter your name: jxh
# Hello, jxh!

上述代码有一个瑕疵,当你只输入一个空格字符时,该程序也能接受,但可以用如下方法进行修复:

while not name or name.isspace()
# 或者
while not name.strip()

# 其中strip()函数是python字符串函数,对字符串进行操作。
# 功能:去掉字符串头尾指定的字符或字符序列。当参数没有时,去掉首尾空白字符。
# 语法格式
string.strip(str)

# 无参数时
name = ' liqin \n'
print(name)
# liqin \n
print(name.strip())
# liqin

# 有参数时
name = 'zzzliqinzzz'
print(name)
# zzzliqinzzz
print(name.strip('z'))
# liqin

for 循环

基本上,可迭代对象是可使用for循环进行遍历的对象,就目前而言,只需将迭代对象视为序列即可。

words = ['this', 'is', 'an', 'ex', 'parrot']
for word in words:
    print(word)
    
#-------------------------------------------
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9]
for number in numbers:
    print(number)

鉴于迭代(也就是遍历)特定范围内的数是一种常见的任务,Python提供了一个创建范围的内置函数。

print(range(0, 10))
# range(0, 10)
print(list(range(0, 10)))
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# 打印数1-100
for number in range(1,101):
    print(number)

迭代字典
d = {'x' : 1, 'y' : 2, 'z' : 3}
for key in d:
    print(key, 'corresponds to', d[key])

for key,value in d.items():
    print(key, 'corresponds to', value)

一些迭代工具
  1. 并行迭代

    并行迭代的内置函数zip,它将两个序列“缝合”起来,并返回一个由元组组成的序列。返回值是一个适合迭代的对象,要查看其内容,可使用list将其转换为列表。

    # 同时迭代如下两个序列
    names = ['anne', 'beth', 'george', 'damon']
    ages = [12, 45, 32, 102]
    # 同时打印名字和对应的年龄
    for i in range(len(names)):
        print(names[i], 'is', ages[i], 'years old')
    '''
    anne is 12 years old
    beth is 45 years old
    george is 32 years old
    damon is 102 years old
    '''
    print(list(zip(names,ages)))
    # [('anne', 12), ('beth', 45), ('george', 32), ('damon', 102)]
    
    # 用zip“缝合”后,可在循环中将元组解包
    for name, age in zip(names, ages):
        print(name, 'is', age, 'years old')
    
    
    

    函数zip可用于“缝合”任意数量的序列。当序列长度不同,函数zip将在最短的序列用完后停止“缝合”

    print(list(zip(range(5), range(100000))))
    # [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
    
    
  2. 迭代时获取索引

    index = 0
    for string in strings:
        if 'xxx' in string:
            strings[index] = '[censored]'
        index += 1
        
    # 也可以使用内置函数enumerate
    for index, string in enumerate(strings):
        if 'xxx' in string:
            strings[index] = '[censored]'
    # 这个函数让你能够迭代索引-值对,其中的索引是自动提供的。
    
    
  3. 反向迭代和排序后再迭代

    reversed和sorted函数,可用于任何序列或迭代的对象,且不就地修改对象,而是返回反转和排序后的版本。

    print(sorted([4, 3, 6, 8, 3]))
    print(sorted('Hello, World!')) 
    # 排序时,可将其转为小写 如sorted('aBc', key = str.lower)
    # 返回['a', 'B', 'c']
    print(reversed('Hello World!'))
    print(list(reversed('Hello World!')))
    print(''.join(reversed('Hello, world!')))
    '''
    [3, 3, 4, 6, 8]
    [' ', '!', ',', 'H', 'W', 'd', 'e', 'l', 'l', 'l', 'o', 'o', 'r']
    <reversed object at 0x000001B86C3DDFA0>
    ['!', 'd', 'l', 'r', 'o', 'W', ' ', 'o', 'l', 'l', 'e', 'H']
    !dlrow ,olleH
    '''
    
    
跳出循环
  1. break

    # 找出小于100的最大平方值
    for math import sqrt
    for n in range(99, 0, -1):
        root = sqrt(n)
        if root = int(root):
            print(n)
            break
    # range第三个参数为步长,即序列中相邻数差
    
    
  2. continue

    用于结束当前迭代,并跳进下一次迭代开头,这意味着跳过循环体中余下的语句,但不结束循环。

  3. while True/break成例

    while True:
        word = input('Please enter a word: ')
        if not word: break
    	# 使用这个单词做一些事情:
        print('The word was ',word)
    
    
循环中的else子句
from math import sqrt
for n in range(99, 81, -1):
    root = sqrt(n)
    if root == int(root):
        print(n)
        break
else:
    print('Didn't find it!)

简单的推导

列表推导是一种从其他列表创建列表的方式,类似于数学中的集合推导。

print([x * x for x in range(10)])
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

# 打印能被3整除的平方值
print([x * x for x in range(10) if x % 3 == 0])
# [0, 9, 36, 81]

print([(x, y) for x in range(3) for y in range(3)])
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
# 上述可使用如下两个for循环创建同样的列表
result = []
for x in range(3):
    for y in range(3):
        result.append((x,y))

三人行

三大语句:pass、del和exec

什么都不做
if name == 'Ralph Auldus Melish':
    print('Welcome!')
elif name == 'Enid':
    pass
elif name == 'Bill Gates':
    print('Access Denied')

使用del删除
scoundrel = {'age' : 42, 'first name': 'Robin', 'last name': 'of Locksley'}
robin = scoundrel
print(scoundrel)
print(robin)
scoundrel = None
print(scoundrel)
print(robin)
robin = None
print(robin)
'''
{'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
{'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
None
{'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
None
'''

如上代码,robin和scoundrel指向同一个字典,因此将None赋给scoundrel后,依然可以通过robin来访问这个字典。但将robin也设置为None之后,这个字典就漂浮再计算机内存中,没有任何名称与之相关联,再也无法获取或使用它了。因此Python直接将其删除,这被成为垃圾收集。同样也可将任何值赋给两个变量,这样的字典也将消失。

另一种办法是使用del语句,这不仅会删除到对象的引用,还会删除名称本身。

x = 1
del x
print(x)
# NameError: name 'x' is not defined

x = ["Hello", "world"]
y = x
y[1] = "Python"
print(x)
del x
print(y)
# ['Hello', 'Python']
# ['Hello', 'Python']
# 其中你只删除名称x,而没有删除列表本身(值),因此删除x对y没有任何影响,虽然x和y都指向同意列表。事实上,在Python中,根本就没有办法删除值,因为你不再使用的值,Python解释器会立即将其删除。

使用exec和eval执行字符串及计算结果
  1. exec

    函数exec将字符串作为代码执行。

    exec("print('Hello, world!')") # Hello, world!
    
    

    然而调用函数exec时只给它提供一个参数绝非好事。在大多数情况下,还应向它传递一个命名空间——用于放置变量的地方;否则代码将污染你的命名空间,即修改你的变量。例如,假设代码使用了名称sqrt。

    from math import sqrt
    exec("sqrt = 1")
    sqrt(4)
    '''
    Traceback (most recent call last):
      File "c:\Users\23850\Desktop\Python\Day01.py", line 441, in <module>
        sqrt(4)
    TypeError: 'int' object is not callable
    '''
    
    

    因此需要一个字典以充当命名空间即作用域,就目前而言,你可将命名空间视为放置变量的地方,类似于一个看不见的字典。因此当你执行赋值语句x=1时,将在当前命名空间存储键x和值1.当前命名空间通常是全局命名空间,但并非必然如此。

    为此,你添加第二个参数——字典,用作代码字符串的命名空间。而实际上,可向exec提供两个命名空间:一个全局的和一个局部的。提供全局的命名空间必须是字典,而提供的局部命名空间可以是任何映射,这一点也适用于eval。

    from math import sqrt
    scope = {}
    exec('sqrt = 1',scope)
    print(sqrt(4))
    # 2.0
    print(scope['sqrt'])
    # 1
    
    

    如你所见,可能带来破坏的代码并非覆盖函数sqrt。函数sqrt该怎么样还怎么样,而通过exec执行赋值语句创建的变量位于scope中。

    如果你将scope打印出来,将发现它包含了很多的内容,这是因为其自动添加了包含所有内置函数和值的字典_builtins_。

    print(len(scope))
    print(scope.keys())
    # 2
    # dict_keys(['__builtins__', 'sqrt'])
    
    
  2. eval

    eval是一个类似于exec的内置函数。exec执行一系列Python语句,而eval计算用字符串表示的Python表达式的值,并返回结果(exec什么都不返回,因它本身是条语句)。例如,你可使用如下代码来创建一个Python计算器:

    result = eval(input("Enter an arithmetic expression: "))
    print(result)
    # Enter an arithmetic expression: 6 + 18 * 2
    # 42
    
    

    与exec一样,也可向eval提供一个命名空间,虽然表达式通常不会像语句那样给变量重新赋值。

    向exec或eval提供命名空间时,可在使用这个命名空间前在其中添加一些值。

    scope = {}
    scope['x'] = 2
    scope['y'] = 3
    print(eval('x * y', scope))
    
    

    同样,同一个命名空间可用于多次调用exec或eval。

    scope = {}
    exec('x = 2', scope)
    result = eval('x * x',scope)
    print(result)
    # 4
    
    

抽象

懒惰是一种美德
# 计算斐波那契数
fibs = [0, 1]
for i in range(8):
    fibs.append(fibs[-2] + fibs[-1])
print(fibs)

# 上述是一次计算前10个斐波那契数,但可修改前述for循环,使其处理动态的范围,即让用户指定最终要得到的序列的长度。
fibs = [0, 1]
num = int(input('How many Fibonacci numbers do you want?'))
for i in range(num-2):
    fibs.append(fibs[-2] + fibs[-1])
print(fibs)

自定义函数

函数执行特定的操作并返回一个值,你可以调用它(调用时可能需要提供一些参数——放在圆括号里)。一般而言,要判断某个对象是否可调用,可使用内置函数callable。

import math
x = 1
y = math.sqrt
print(callable(x)) 
print(callable(y))

# False
# True

函数是结构化编程的核心。我们可使用def(表示定义函数)语句。

def hello(name):
    return 'Hello, ' + name + '!' 

运行上述代码,将有一个名为hello的新函数。它会返回一个字符串,其中包含向唯一参数指定的人发出的问候语。可像使用内置函数那样使用这个函数。

print(hello('world'))
print(hello('Gumby'))
# Hello, world!
# Hello, Gumby!

给函数编写文档

编写函数编写文档,可添加注释(以#打头的内容)。还有就是添加独立的字符串。在有些地方,如def语句后面(以及模块和类的开头),添加这样的字符串很有用。放在函数开头的字符串称为文档字符串,将作为函数一部分存储起来。如下:

def square(x):
    'Calculates the square of the number x'
    return x * x

print(square.__doc__)
# Calculates the square of the number x

其中特殊的内置函数help很有用。在交互式解释器中,可使用它来获取有关函数的信息,其中包括函数的文档字符串。

print(help(square))

'''
Help on function square in module __main__:

square(x)
    Calculates the square of the number x

None
'''
其实并不是函数的函数

数学意义上的函数总是返回根据参数计算得到的结果。在Python中,有些函数什么都不返回。在诸如Pascal等语言中,这样的函数可能另有其名(如过程),但在Python中,函数就是函数,即使它严格来说并非函数。什么都不返回的函数不包含return语句,或者包含return语句,但没有在return后面指定值。

def test():
    print('This is printed')
    return 
	print('This is not')
# 这里的return语句只是为了结束函数,其实如上述代码,会编译错误
# TabError: inconsistent use of tabs and spaces in indentation
x = test()
print(x)
# This is printed
# None

在打印输出x后,出现了None这个熟悉的值。由此可知,所有函数都有返回值。如果没有告诉他们返回什么,将返回None。

参数魔法
我能修改参数吗

在函数内部给参数赋值对外部没有任何影响。

def try_to_change(n):
    n = 'Mr.Gumby'
name = 'Mrs.Entity'
try_to_change(name)
print(name)
# Mrs.Entity

在try_to_change内,将新值赋给了参数n,但如你所见,这对变量name没有影响。说到底,这是两个完全不同的变量。上述代码效果如下:

name = 'Mrs.Entity'
n = name
n = 'Mr.Gumby'
print(name)
# Mrs.Entity

n = name使得n和name关联到一起,都指向存储’Mrs.Entity’字符串的内存。但n = 'Mr.Gumby’使得n指向存储’Mr.Gumby’字符串的内存,从而不影响name指向的值。由此可见,在函数内部重新关联参数(即给它赋值)时,函数外部的变量不受影响。

字符串(以及数和元组)是不可改变的,这意味着你不能修改他们(即只能替换为新值)。因此这些类型作为参数没有什么可说的。但如果参数是可变的数据结构(如列表)呢?

def change(n):
    n[0] = 'Mr.Gumby'
names = ['Mrs.Entity', 'Mrs.Thing']
change(names)
print(names)
# ['Mr.Gumby', 'Mrs.Thing']

若要避免这样的结果,必须创建列表的副本。对序列执行裂片操作时,返回的切片都是副本。

n = names[:]
# 或者
n = names.copy()

# 现在n和names包含两个相等但不同的列表。
print(n is names)
print(n == names)
# False
# True

现在如果(像在函数change中那样)修改n,将不会影响names。

n[0] = 'Mr.Gumby'
print(n)
print(names)

change(names[:])
print(names)
# ['Mr.Gumby', 'Mrs.Thing']
# ['Mrs.Entity', 'Mrs.Thing']
# ['Mrs.Entity', 'Mrs.Thing']

  1. 为什么要修改参数

    在提高程序的抽象方面,使用函数来修改数据结构(如列表或字典)是一种不错的方式。

    # 编写一个程序,让它存储姓名,并让用户根据名字、中间名或姓找人。
    
    # 初始化数据结构 
    def init(data):
        data['first'] = {}
        data['middle'] = {}
        data['last'] = {}
        
    # 数据结构data是一个字典,包含三个键:'first','middle'和'last'。在每个键下都存储一个字典。这些字典的键为姓名(名字、中间名或姓),而值为人员列表。
    
    # 获取人员姓名的函数
    def lookup(data, label, name):
        return data[label].get(name)
    
    
    # 存储人员到数据结构中
    def store(data, full_name):
        names = full_name.split()
        if len(names) == 2: names.insert(1," ")
        labels = 'first', 'middle', 'last'
    
        for label, name in zip(labels, names):
            people = lookup(data, label, name)
            if people:
                people.append(full_name)
                print("-----------",full_name)
            else:
                data[label][name] = [full_name]
                print("************",[full_name])
    
    MyNames = {}
    init(MyNames)
    store(MyNames, "Magnus Lie Hetland")
    result1 = lookup(MyNames, 'middle', 'Lie')
    print(result1)
    
    store(MyNames, "Robin Hood")
    store(MyNames, "Robin Locksley")
    result2 = lookup(MyNames, 'first', 'Robin')
    print(result2)
    
    store(MyNames, 'Mr. Gumby')
    result3 = lookup(MyNames, 'middle', ' ')
    print(result3)
    
    '''
    -------------------------------------
    ************ ['Magnus Lie Hetland']
    ************ ['Magnus Lie Hetland']
    ************ ['Magnus Lie Hetland']
    ['Magnus Lie Hetland']
    ************ ['Robin Hood']
    ************ ['Robin Hood']
    ************ ['Robin Hood']
    ----------- Robin Locksley
    ----------- Robin Locksley
    ************ ['Robin Locksley']
    ['Robin Hood', 'Robin Locksley']
    ************ ['Mr. Gumby']
    ----------- Mr. Gumby
    ************ ['M. Gumby']
    ['Robin Hood', 'Robin Locksley', 'Mr. Gumby']
    '''
    
    
  2. 如果参数是不可变的

    在有些语言(如C++、Pascal和Ada)中,经常需要给参数赋值并让这种修改影响函数外部的变量。在Python中,没法直接这样做,只能修改参数对象本身。但如果参数是不可变的呢?

    在这种情况下,应从函数返回所需要的值(如果要返回多个值,就以元组方式返回他们)。例如,可以像下面这样编写将变量的值加1:

    def inc(x):
        return x + 1
    foo = 10
    foo = inc(foo)
    print(foo) 
    # 11
    
    

    如果一定要修改参数,可将值放入列表中,如下:

    def inc(x):
        x[0] = x[0] + 1
    foo = [10]
    inc(foo)
    print(foo)
    # [11]
    
    
关键字参数和默认值
def hello(gretting, name):
    print('{}, {}!'.format(gretting, name))
    
hello(name='world', gretting = 'Hello')
# Hello, world!

如上述,这样使用名称指定的参数称为关键字参数。

像这样给参数指定默认值后,调用函数时可不提供它!根据需要,一个参数也不提供、提供部分参数值或提供全部参数值。

hello()
hello('jxh')
hello('Greeting', 'universe')

若只想提供参数name,并让greeting 使用默认值。

hello(name = 'Gumby')

收集参数
def print_params(*params):
    print(params)
    
print_params('Testing')
# ('Testing',)

这里打印的说一个元组,因为里面有一个逗号。

print_params(1, 2, 3)
# (1, 2, 3)

因此星号意味着收集余下的位置参数。如果没有可收集的参数,params将是一个空元组。

print_params()
# ()

与赋值一样,带星号的参数可放在其他位置(而不是最后),但不同的是,在这种情况下你需要做些额外的工作:使用名称来指定后续的参数。

def in_the_middle(x, *y, z):
    print(x, y, z)

in_the_middle(1, 2, 3, 4, 5, z=7)

# 1 (2, 3, 4, 5) 7

星号不会收集关键字参数。

要收集关键字参数,可使用两个星号。

def print_params_1(**params):
    print(params)
print_params_1(x=1, y=2, z=3)

# {'x': 1, 'y': 2, 'z': 3}

def print_params_2(x, y, z=3, *pospar, **keypar):
    print(x, y, z)
    print(pospar)
    print(keypar)
print_params_2(1, 2, 3, 5, 6, 7, foo=1, bar=2)

# 1 2 3
# (5, 6, 7)
# {'foo': 1, 'bar': 2}

print_params_2(1, 2)

# 1 2 3
# ()
# {}

现在回到最初的问题:如何在姓名存储示例中使用这种技术?

def store_1(data, *full_names):
    for full_name in full_names:
         names = full_name.split()
         if len(names) == 2: names.insert(1," ")
         labels = 'first', 'middle', 'last'

         for label, name in zip(labels, names):
             people = lookup(data, label, name)
             if people:
                people.append(full_name)
                print("-----------",full_name)
             else:
                data[label][name] = [full_name]
                print("************",[full_name])

d = {}
init(d)
store_1(d, 'Luke Skywalker', 'Anakin Skywalker')
print(lookup(d, 'last', 'Skywalker'))

# ['Luke Skywalker', 'Anakin Skywalker']
分配参数

——与收集参数相反的操作。

def add(x, y):
    return x + y
params = (1, 2)
# 这与前面执行的操作差不多是反的:不是收集参数,而是分配参数。这是通过在调用函数(而不是定义函数)时使用运算符*实现的。
result = add(*params)
print(result)
# 3
def hello(greeting, name):
    print('{}, {}!'.format(greeting, name))
params = {'name': 'Sir Robin', 'greeting': 'Well met'}
hello(**params)

# Well met, Sir Robin!
作用域

变量到底是什么呢?可将其视为指向值的名称。因此,执行赋值语句x = 1后,名称x指向值1。这几乎与使用字典时一样(字典中的键指向值),只是你使用的是”看不见“的字典。有一个名为vars的内置函数,它返回这个不可见的字典:

x = 1
scope = vars()
print(scope['x'])
# 1
scope['x'] += 1
print(x)
# 2

一般而言,不应该修改vars返回的字典,根据官方文档,这样做的结果是不确定的。换言之,可能得不到想要的结果。这种”看不见的字典“称为命名空间或作用域。那么有多少命名空间呢?除全局作用域外,每个函数调用都将创建一个。

def combine(parameter):
    print(parameter + external)
external = 'berry'
combine('Shrub')
# Shrubberry

在函数内读取全局变量通常不会出现问题,但若有一个局部变量或参数与你要访问的全局变量同名,就无法直接访问全局变量,因为它被局部变量”遮住“了。如果需要,可使用函数globals来访问全局变量。这个函数类似于vars,返回一个包含全局变量的字典。(locals返回一个包含局部变量的字典)

def combine(parameter):
    print(parameter + globals()['parameter']) # 使用globals()['parameter']来访问全局变量parameter
parameter = 'berry'
combine('Shrub')
# Shrubberry

重新关联全局变量(使其指向新值)是另一码事。在函数内部给变量赋值时,该变量默认为局部变量,你得明确告诉Python是全局变量,如下:

x = 1
def change_global():
    global x
    x = x + 1
change_global()
print(x)
# 2

  • 作用域嵌套

    Python函数可以嵌套,即将一个函数放在另一个函数内,如下:

    def foo():
        def bar():
            print("Hello, world!")
            bar()
    
    
    def multiplier(factor):
        def multiplyByFactor(number):
            return number*factor
        return multiplyByFactor
    double = multiplier(2)
    print(double(5)) # 10
    
    triple = multiplier(3)
    print(triple(3)) # 9
    
    print(multiplier(5)(4)) # 20
    
    
递归

如果你想的是对你有所帮助的递归函数,这样的递归函数通常包含下面两个部分:

  1. 基线条件(针对最小的问题):满足这种条件时函数将直接返回一个值。
  2. 递归条件:包含一个或多个调用,这些调用旨在解决问题的一部分。
两个经典的案例:阶乘和幂

计算数字n的阶乘。

def factorial(n):
    result = n
    for i in range(1, n):
        result *= i
    return result
def factorial(n):
    if n == 1:
        return 1
    else:
        return n * factorial(n-1)

计算幂。

def power(x, n):
    result = 1
    for i in range(n):
        result *= x
    return result
def power(x, n):
    if n == 0:
        return 1
    else:
        return x * power(x, n -1)
另一个案例:二分查找
def search(sequence, number, lower, upper):
    if lower == upper:
        assert number == sequence[upper]
        return upper
    else:
        middle = (lower + upper) // 2
        if number > sequence[middle]:
            return search(sequence, number, middle + 1, upper)
        else:
            return search(sequence, number, lower, middle)

关于assert:断言函数是对表达式布尔值的判断,要求表达式计算值必须为真。可用于自动调试。

如果表达式为假,触发异常;如果表达式为真,不执行任何操作。

关于//: // 返回的是整数结果(可以理解为/的整数部分)

  • 函数式编程

    在Python中,通常不会如此倚重函数(而是创建自定义对象),但完全可以这样做。

    Python提供了一些有助于进行这种函数式编程的函数:map、filter和reduce。但在较新的Python版本中,函数map和filter的用处不大,应该用列表推导来替换它们。

    list(map(str,range(10))) # 与[str(i) for i in range(10)]等价
    
    # 你可以使用filter根据布尔函数的返回值来对元素进行过滤
    def fun(x):
        return x.isalnum()
    seq = ["foo","x41","?!","****"]
    print(list(filter(func,seq)))
    
    # ['foo', 'x41']
    # 就上述示例而言,如果转而使用列表来推导,就无需创建前述自定义函数。
    [x for x in seq if x.isalnum()]
    

    实际上,Python提供了一种名为lambda表达式的功能,让你能创建内嵌的简单函数(主要提供map、filter和reduce使用)。

    filter(lambda x: x.isalnum(), seq)
    

    如果你要将序列中的所有数都相加,可结合reduce和lambda x, y: x+y

    numbers = [3, 34, 43, 2, 12, 13, 64, 77, 55]
    from functools import reduce
    reduce(lambda x, y: x + y, number)
    

    关于reduce、filter、map内置函数

    filter(function, sequence):对sequence中的item依次执行function(item),将执行结果为True的item组成一个List/String/Tuple(取决于sequence的类型)。

    filter(function or None, sequence) -> list, tuple, or string:入参为函数和列表/元组/字符串,返回值为item列表/元组/字符串。

    map(function, sequence) :对sequence中的item依次执行function(item),将执行结果function(item)组成一个List返回。

    map(function, sequence[, sequence, …]) -> list:入参是为函数和列表/元组/字符串,返回值为function(item)列表。

    reduce(function, sequence, starting_value):对sequence中的item顺序迭代调用function,如果有starting_value,还可以作为初始值调用。function接收的参数个数只能为2,先把sequence中第一个值和第二个值当参数传给function,再把function的返回值和第三个值当参数传给function,然后只返回一个结果。

    reduce(function, sequence[, initial]) -> value:入参是为函数和列表/元组/字符串和初始值,返回值为数值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值