Python 补充 【有问题的章节】 字符串和编码 
函数参数 
返回函数 
装饰 
Hint parameter: 形参,指的是函数中的参数名称:
1 2 def add(x,y):>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>此处x,y为形参。
argument: 实参,指的是你提供给函数调用的值:
1 2 3 x=1
基本知识 缩进 约定缩进为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.' )'m ok. >>> print(' I\'m learning\nPython.' )'m learning Python. >>> print(' \\\n\\') \ \ 
如果字符串里面有很多字符都需要转义,就需要加很多\,为了简化,Python还允许用r''表示''内部的字符串默认不转义,可以自己试试:
1 2 3 4 >>>  print ('\\\t\\' )>>>  print (r'\\\t\\' )
如果字符串内部有很多换行,用\n写在一行里不好阅读,为了简化,Python允许用'''...'''的格式表示多行内容,可以自己试试:
1 2 3 4 5 6 >>>  print ('''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 ])>>>  s1 , 2 , 3 }
set可以看成数学意义上的无序和无重复元素的集合,因此,两个set可以做数学意义上的交集、并集等操作:
1 2 3 4 5 6 >>>  s1 = set ([1 , 2 , 3 ])>>>  s2 = set ([2 , 3 , 4 ])>>>  s1 & s22 , 3 }>>>  s1 | s21 , 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 ):1 while  n > 0 :1 return  s
power(x, n)函数有两个参数:x和n,这两个参数都是位置参数 (调用函数时,传入的两个值按照位置顺序依次赋给参数x和n)
默认参数 1 2 3 4 5 6 def  power (x, n=2  ):1 while  n > 0 :1 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=[]) '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 :'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 * nreturn  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' )35  other: {'city' : 'Beijing' }>>>  person('Adam' , 45 , gender='M' , job='Engineer' )45  other: {'gender' : 'M' , 'job' : 'Engineer' }
也可以先组装成dict,再传递参数:
1 2 3 >>>  extra = {'city' : 'Beijing' , 'job' : 'Engineer' }>>>  person('Jack' , 24 , **extra)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' )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 :1 for  num in  args: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 :print ('测试失败!' )except  TypeError:print ('测试成功!' )
课后练习 
注释掉的是正确答案,现在还不太懂。
(文件名:ex_function_para.py)
高级特性 切片 注意点 倒数第一位置的索引是-1 
示例 1 2 3 def  is_palindrome (n ): 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 )]>>>  L0 , 1 , 4 , 9 , 16 , 25 , 36 , 49 , 64 , 81 ]>>>  g = (x * x for  x in  range (10 ))>>>  gobject  <genexpr> at 0x1022ef630 >
函数定义中包含yield关键字,成为generator函数,调用一个generator函数将返回一个generator: 
 
1 2 3 4 5 6 7 def  fib (max 0 , 0 , 1 while  n < max :yield  b1 return  'done' 
generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
正确的写法是创建一个generator对象,然后不断对这一个generator对象调用next():
1 2 3 4 5 6 7 8 9 10 >>>  g = odd()>>>  next (g)1 1 >>>  next (g)2 3 >>>  next (g)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 iter ([1 , 2 , 3 , 4 , 5 ])while  True :try :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 6 abs 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'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  + ydef  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 )
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]
字符串排序 默认排序:
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>>>  flambda > at 0x101c6ef28 >>>>  f(5 )25 
装饰器 在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。本质上,decorator就是一个返回函数的高阶函数。
所以,我们要定义一个能打印日志的decorator,可以定义如下:
1 2 3 4 5 def log(func):wrapper (*args, **kw):'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 '  'Michael Liao'  import  sys def  test ():if  len (args)==1 :print ('Hello, world!' )elif  len (args)==2 :print ('Hello, %s!'  % args[1 ])else :print ('Too many arguments!' )if  __name__=='__main__' :
学到面向对象高级编程 2022-7-30
一些实际问题 字符画 现在自己写的尺寸有些问题