Python 补充 【有问题的章节】 字符串和编码
函数参数
返回函数
装饰
Hint parameter: 形参,指的是函数中的参数名称:
1 2 def add(x,y):>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>此处x,y为形参。 return x+y
argument: 实参,指的是你提供给函数调用的值:
1 2 3 x=1 y=2 add(x,y)>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>此处x,y就变为实参了。
基本知识 缩进 约定缩进为4个空格
重构代码时,必须重新检查缩进是否正确
大小写 python大小写敏感
数据表示 进制表示
示例:0xff00
,0xa5b4c3d2
数字中间可以用_
分隔
1 2 10_000_000_000 = 10000000000 0xa1b2_c3d4 = 0xa1b2c3d4
浮点数(也就是小数,之所以称为浮点数,是因为按照科学记数法表示时,一个浮点数的小数点位置是可变的,比如,1.23x10^9和12.3x10^8是完全相等的)
1.23x10^9表示为1.23e9
字符串 字符串是以单引号'
或双引号"
括起来的任意文本
如果字符串内部既包含'
又包含"
怎么办?可以用转义字符\
来标识,比如:
表示的字符串内容是:
转义字符\
可以转义很多字符,比如\n
表示换行,\t
表示制表符,字符\
本身也要转义,所以\\
表示的字符就是\
,可以在Python的交互式命令行用print()
打印字符串看看:
1 2 3 4 5 6 7 8 >>> print ('I\'m ok.' ) I'm ok. >>> print(' I\'m learning\nPython.' ) I'm learning Python. >>> print(' \\\n\\') \ \
如果字符串里面有很多字符都需要转义,就需要加很多\
,为了简化,Python还允许用r''
表示''
内部的字符串默认不转义,可以自己试试:
1 2 3 4 >>> print ('\\\t\\' ) \ \>>> print (r'\\\t\\' ) \\\t\\
如果字符串内部有很多换行,用\n
写在一行里不好阅读,为了简化,Python允许用'''...'''
的格式表示多行内容,可以自己试试:
1 2 3 4 5 6 >>> print ('''line1 ... line2... line3''' ) line1 line2 line3
空值 空值是Python里一个特殊的值,用None
表示。None
不能理解为0
,因为0
是有意义的,而None
是一个特殊的空值。
常量 在Python中,通常用全部大写的变量名表示常量:
但事实上PI
仍然是一个变量,Python根本没有任何机制保证PI
不会被改变,所以,用全部大写的变量名表示常量只是一个习惯上的用法,如果你一定要改变变量PI
的值,也没人能拦住你。
范围 Python的整数没有大小限制,而某些语言的整数根据其存储长度是有大小限制的,例如Java对32位整数的范围限制在-2147483648
-2147483647
。
Python的浮点数也没有大小限制,但是超出一定范围就直接表示为inf
(无限大)。
元组(tuple) 如果要定义一个空的tuple,可以写成()
:
只有1个元素的tuple定义时必须加一个逗号,
,来消除歧义:
Python在显示只有1个元素的tuple时,也会加一个逗号,
,以免你误解成数学计算意义上的括号。
tuple所谓的“不变”是说,tuple的每个元素,指向永远不变。即指向'a'
,就不能改成指向'b'
,指向一个list,就不能改成指向其他对象,但指向的这个list本身是可变的!
不可变对象 对于不变对象来说,调用对象自身的任意方法,也不会改变该对象自身的内容。相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可变的。
字典(dict) dict的key必须是不可变对象 .
在Python中,字符串、整数等都是不可变的,因此,可以放心地作为key。而list是可变的,就不能作为key
集合(set) 要创建一个set,需要提供一个list作为输入集合:
1 2 3 >>> s = set ([1 , 2 , 3 ])>>> s {1 , 2 , 3 }
set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作:
1 2 3 4 5 6 >>> s1 = set ([1 , 2 , 3 ])>>> s2 = set ([2 , 3 , 4 ])>>> s1 & s2 {2 , 3 }>>> s1 | s2 {1 , 2 , 3 , 4 }
set和dict的唯一区别仅在于没有存储对应的value,但是,set的原理和dict一样,所以,同样不可以放入可变对象.
函数
在交互式命令行通过help()
查看函数的帮助信息。
函数名其实就是指向一个函数对象的引用 ,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:
1 2 3 >>> a = abs >>> a(-1 ) 1
返回值 返回单值 如果没有return
语句,函数执行完毕返回None
此外return None
可以简写为return
返回多值 Python的函数返回多值其实就是返回一个tuple
空函数 pass
语句用来作为占位符.
如果想定义一个什么事也不做的空函数,可以用pass
语句:
用于其他语句:
缺少了pass
,代码运行就会有语法错误。
函数的参数 位置参数 1 2 3 4 5 6 def power (x, n ): s = 1 while n > 0 : n = n - 1 s = s * x return s
power(x, n)
函数有两个参数:x
和n
,这两个参数都是位置参数 (调用函数时,传入的两个值按照位置顺序依次赋给参数x
和n
)
默认参数 1 2 3 4 5 6 def power (x, n=2 ): s = 1 while n > 0 : n = n - 1 s = s * x return s
必选参数在前,默认参数在后 ,默认参数必须指向不变对象!
示例:
1 2 3 4 5 6 7 8 def enroll (name, gender, age=6 , city='Beijing' ): print ('name:' , name) print ('gender:' , gender) print ('age:' , age) print ('city:' , city) 调用1 :enroll('Bob' , 'M' , 7 ) 调用2 :enroll('Adam' , 'M' , city='Tianjin' )
【关于默认参数不变性的理解】 先定义一个函数,传入一个list,添加一个END
再返回:
1 2 3 def add_end (L=[]) : L.append('END' ) return L
当你正常调用时,结果似乎不错:
1 2 3 4 >>> add_end ([1, 2, 3] )[1, 2, 3, 'END' ] >>> add_end (['x' , 'y' , 'z' ] )['x' , 'y' , 'z' , 'END' ]
当你使用默认参数调用时,一开始结果也是对的:
但是,再次调用add_end()
时,结果就不对了:
1 2 3 4 >>> add_end ()['END' , 'END' ] >>> add_end ()['END' , 'END' , 'END' ]
很多初学者很疑惑,默认参数是[]
,但是函数似乎每次都“记住了”上次添加了'END'
后的list。
原因解释如下:
Python函数在定义的时候,默认参数L
的值就被计算出来了,即[]
,因为默认参数L
也是一个变量,它指向对象[]
,每次调用该函数,如果改变了L
的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]
了。
定义默认参数要牢记一点:默认参数必须指向不变对象 !
要修改上面的例子,我们可以用None
这个不变对象来实现:
1 2 3 4 5 def add_end (L=None ): if L is None : L = [] L.append('END' ) return L
现在,无论调用多少次,都不会有问题:
1 2 3 4 >>> add_end ()['END' ] >>> add_end ()['END' ]
为什么要设计str
、None
这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。
可变参数 可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。
1 2 3 4 5 6 7 8 9 10 def calc (*numbers ): sum = 0 for n in numbers: sum = sum + n * n return sum >>> calc(1 , 2 )5 >>> calc()0
list/tuple做可变参数 在list或tuple前面加一个*
号,把list或tuple的元素变成可变参数传进去:
1 2 3 >>> nums = [1 , 2 , 3 ]>>> calc(*nums)14
关键字参数 允许传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
1 2 3 4 5 6 7 def person (name, age, **kw ): print ('name:' , name, 'age:' , age, 'other:' , kw)>>> person('Bob' , 35 , city='Beijing' ) name: Bob age: 35 other: {'city' : 'Beijing' }>>> person('Adam' , 45 , gender='M' , job='Engineer' ) name: Adam age: 45 other: {'gender' : 'M' , 'job' : 'Engineer' }
也可以先组装成dict,再传递参数:
1 2 3 >>> extra = {'city' : 'Beijing' , 'job' : 'Engineer' }>>> person('Jack' , 24 , **extra) name: Jack age: 24 other: {'city' : 'Beijing' , 'job' : 'Engineer' }
命名关键字参数 需要限制关键字名字时使用。
1 2 3 4 5 6 def person (name, age, *, city, job ): print (name, age, city, job) >>> person('Jack' , 24 , city='Beijing' , job='Engineer' ) Jack 24 Beijing Engineer
如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*
了:
1 2 def person (name, age, *args, city, job ): print (name, age, args, city, job)
参数顺序 参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数
对于任意函数,都可以通过类似func(*args, **kw)
的形式调用它,无论它的参数是如何定义的。
遗留的问题【等学完了错误处理再回来解决】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import mathdef mul (*args ): if len (args) == 0 : print (123213 ) return TypeError("Lack of numbers" ) else : ans = 1 for num in args: ans = ans * num return ansprint ('mul(5) =' , mul(5 ))print ('mul(5, 6) =' , mul(5 , 6 ))print ('mul(5, 6, 7) =' , mul(5 , 6 , 7 ))print ('mul(5, 6, 7, 9) =' , mul(5 , 6 , 7 , 9 ))if mul(5 ) != 5 : print ('测试失败!' )elif mul(5 , 6 ) != 30 : print ('测试失败!' )elif mul(5 , 6 , 7 ) != 210 : print ('测试失败!' )elif mul(5 , 6 , 7 , 9 ) != 1890 : print ('测试失败!' )else : try : mul() print ('测试失败!' ) except TypeError: print ('测试成功!' )
课后练习
注释掉的是正确答案,现在还不太懂。
(文件名:ex_function_para.py)
高级特性 切片 注意点 倒数第一位置的索引是-1
示例 1 2 3 def is_palindrome (n ): l = [ch for ch in str (n)] return l == l[::-1 ]
迭代 通过for
循环来遍历这个list
或tuple
,这种遍历我们称为迭代(Iteration)
通过collections.abc
模块的Iterable
类型判断对象是否可迭代:
1 2 3 4 5 6 7 >>> from collections.abc import Iterable>>> isinstance ('abc' , Iterable) True >>> isinstance ([1 ,2 ,3 ], Iterable) True >>> isinstance (123 , Iterable) False
1 2 3 4 5 6 7 >>> for i, value in enumerate (['A' , 'B' , 'C' ]): ... print (i, value) ...0 A1 B2 C
列表生成式
1 2 3 4 5 6 >>> [x * x for x in range (1 , 11 )] [1 , 4 , 9 , 16 , 25 , 36 , 49 , 64 , 81 , 100 ]>>> [x * x for x in range (1 , 11 ) if x % 2 == 0 ] [4 , 16 , 36 , 64 , 100 ]>>> [m + n for m in 'ABC' for n in 'XYZ' ] ['AX' , 'AY' , 'AZ' , 'BX' , 'BY' , 'BZ' , 'CX' , 'CY' , 'CZ' ]
1 2 3 >>> import os >>> [d for d in os.listdir('.' )] ['.emacs.d' , '.ssh' , '.Trash' , 'Adlm' , 'Applications' , 'Desktop' , 'Documents' , 'Downloads' , 'Library' , 'Movies' , 'Music' , 'Pictures' , 'Public' , 'VirtualBox VMs' , 'Workspace' , 'XCode' ]
在一个列表生成式中,for
前面的if ... else
是表达式,而for
后面的if
是过滤条件,不能带else
。
1 2 3 4 5 >>> [x for x in range (1 , 11 ) if x % 2 == 0 ] [2 , 4 , 6 , 8 , 10 ]>>> [x if x % 2 == 0 else -x for x in range (1 , 11 )] [-1 , 2 , -3 , 4 , -5 , 6 , -7 , 8 , -9 , 10 ]
2022-7-31 复习到generator
生成器 Python中,这种一边循环一边计算的机制,称为生成器:generator
把一个列表生成式的[]
改成()
,就创建了一个generator:
1 2 3 4 5 6 >>> L = [x * x for x in range (10 )]>>> L [0 , 1 , 4 , 9 , 16 , 25 , 36 , 49 , 64 , 81 ]>>> g = (x * x for x in range (10 ))>>> g <generator object <genexpr> at 0x1022ef630 >
函数定义中包含yield
关键字,成为generator函数,调用一个generator函数将返回一个generator:
1 2 3 4 5 6 7 def fib (max ): n, a, b = 0 , 0 , 1 while n < max : yield b a, b = b, a + b n = n + 1 return 'done'
generator的函数,在每次调用next()
的时候执行,遇到yield
语句返回,再次执行时从上次返回的yield
语句处继续执行。
正确的写法是创建一个generator对象,然后不断对这一个generator对象调用next()
:
1 2 3 4 5 6 7 8 9 10 >>> g = odd()>>> next (g) step 1 1 >>> next (g) step 2 3 >>> next (g) step 3 5
2022-7-27 学到迭代器
可迭代对象 可以直接作用于for
循环的对象统称为可迭代对象 :Iterable
,可以使用isinstance()
判断一个对象是否是Iterable
对象:
1 2 3 4 5 6 7 8 9 10 11 >>> from collections.abc import Iterable>>> isinstance ([], Iterable)True >>> isinstance ({}, Iterable)True >>> isinstance ('abc' , Iterable)True >>> isinstance ((x for x in range (10 )), Iterable)True >>> isinstance (100 , Iterable)False
可迭代对象包括:
迭代器 可以被next()
函数调用并不断返回下一个值的对象称为迭代器 :Iterator
。可以使用isinstance()
判断一个对象是否是Iterator
对象:
1 2 3 4 5 6 7 8 9 >>> from collections.abc import Iterator>>> isinstance ((x for x in range (10 )), Iterator)True >>> isinstance ([], Iterator)False >>> isinstance ({}, Iterator)False >>> isinstance ('abc' , Iterator)False
生成器都是Iterator
对象,但list
、dict
、str
虽然是Iterable
,却不是Iterator
把list
、dict
、str
等Iterable
变成Iterator
可以使用iter()
函数:
1 2 3 4 >>> isinstance (iter ([]), Iterator)True >>> isinstance (iter ('abc' ), Iterator)True
Python的for
循环本质上就是通过不断调用next()
函数实现的,例如:
1 2 for x in [1 , 2 , 3 , 4 , 5 ]: pass
实际上完全等价于:
1 2 3 4 5 6 7 8 9 10 it = iter ([1 , 2 , 3 , 4 , 5 ])while True : try : x = next (it) except StopIteration: break
函数式编程 变量指向函数 1 2 3 >>> f = abs >>> f(-10 )10
函数名其实就是指向函数的变量!
高阶函数 一个函数就可以接收另一个函数作为参数 ,这种函数就称之为高阶函数
示例:
1 2 def add (x, y, f ): return f(x) + f(y)
调用add(-5, 6, abs)
时,参数x
,y
和f
分别接收-5
,6
和abs
,计算过程为:
1 2 3 4 5 x = -5 y = 6 f = abs f(x) + f(y) ==> abs (-5 ) + abs (6 ) ==> 11 return 11
map map()
函数接收两个参数,一个是函数,一个是Iterable
(可迭代对象),map
将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator
(迭代器)返回
示例:
1 2 3 4 5 6 7 >>> r = map (f, [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ])>>> list (r) [1 , 4 , 9 , 16 , 25 , 36 , 49 , 64 , 81 ]>>> list (map (str , [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ])) ['1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' ]
reduce reduce
把一个函数作用在一个序列[x1, x2, x3, ...]
上,这个函数必须接收两个参数,reduce
把结果继续和序列的下一个元素做累积计算,其效果就是:
1 reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
示例:
1 2 3 4 5 6 7 8 9 10 from functools import reduce DIGITS = {'0' : 0 , '1' : 1 , '2' : 2 , '3' : 3 , '4' : 4 , '5' : 5 , '6' : 6 , '7' : 7 , '8' : 8 , '9' : 9 }def str2int (s ): def fn (x, y ): return x * 10 + y def char2num (s ): return DIGITS[s] return reduce(fn, map (char2num, s))
filter 示例:删掉一个序列中的空字符串
1 2 3 4 5 def not_empty (s ): return s and s.strip()list (filter (not_empty, ['A' , '' , 'B' , None , 'C' , ' ' ]))
filter()
函数返回的是一个Iterator
,也就是一个惰性序列,所以要强迫filter()
完成计算结果,需要用list()
函数获得所有结果并返回list。
【待理解:filter实现素数筛子(ex_filter_prime_sieve.py)】 sorted sorted()
函数可以接收一个key
函数来实现自定义的排序
示例:
按绝对值大小排序: 1 2 >>> sorted ([36 , 5 , -12 , 9 , -21 ], key=abs ) [5, 9, -12, -21, 36]
key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。对比原始的list和经过key=abs
处理过的list:
1 2 3 list = [36 , 5 , -12 , 9 , -21 ]keys = [36 , 5 , 12 , 9 , 21 ]
然后sorted()
函数按照keys进行排序,并按照对应关系返回list相应的元素:
1 2 3 keys排序结果 => [5, 9, 12, 21, 36] | | | | | 最终结果 => [5, 9, -12, -21, 36]
字符串排序 默认排序:
1 2 >>> sorted (['bob' , 'about' , 'Zoo' , 'Credit' ] )['Credit' , 'Zoo' , 'about' , 'bob' ]
默认情况下,对字符串排序,是按照ASCII的大小比较的,由于'Z' < 'a'
,结果,大写字母Z
会排在小写字母a
的前面。
现在,我们提出排序应该忽略大小写,按照字母序排序。要实现这个算法,不必对现有代码大加改动,只要我们能用一个key函数把字符串映射为忽略大小写排序即可。忽略大小写来比较两个字符串,实际上就是先把字符串都变成大写(或者都变成小写),再比较。
这样,我们给sorted
传入key函数,即可实现忽略大小写的排序:
1 2 >>> sorted (['bob' , 'about' , 'Zoo' , 'Credit' ], key=str .lower) ['about' , 'bob' , 'Credit' , 'Zoo' ]
要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True
:
1 2 >>> sorted(['bob' , 'about' , 'Zoo' , 'Credit' ], key =str.lower, reverse =True ) ['Zoo' , 'Credit' , 'bob' , 'about' ]
看到返回函数 2022-7-28
函数作为返回值 匿名函数(lambda) Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction)
关键字lambda
表示匿名函数,冒号前面的x
表示函数参数。
匿名函数有个限制,就是只能有一个表达式,不用写return
,返回值就是该表达式的结果。
可以把匿名函数赋值给一个变量,再利用变量来调用该函数:
1 2 3 4 5 >>> f = lambda x: x * x>>> f <function <lambda > at 0x101c6ef28 >>>> f(5 )25
装饰器 在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。本质上,decorator就是一个返回函数的高阶函数。
所以,我们要定义一个能打印日志的decorator,可以定义如下:
1 2 3 4 5 def log(func): def wrapper (*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper
偏函数 采用functools.partial
创建。
functools.partial
的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
示例:
1 2 3 4 5 6 >>> import functools>>> int2 = functools.partial(int , base=2 )>>> int2('1000000' )64 >>> int2('1010101' )85
模块 使用模块的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ' a test module ' __author__ = 'Michael Liao' import sys def test (): args = sys.argv if len (args)==1 : print ('Hello, world!' ) elif len (args)==2 : print ('Hello, %s!' % args[1 ]) else : print ('Too many arguments!' )if __name__=='__main__' : test()
学到面向对象高级编程 2022-7-30
一些实际问题 字符画 现在自己写的尺寸有些问题