Python学习笔记

教程总纲:http://www.runoob.com/python/python-tutorial.html

进阶教程http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000

基本概念:(按照Python3标准来开发

按照缩进来区分代码块

python默认不支持中文,采用 ASCII 编码,如果需要使用中文(特别要小心中文的标点等,也会引起运行时错误),那么可以参考文章:http://www.jb51.net/article/26298.htm

python是顺序执行的脚本,它不在乎变量的声明位置,只要在使用这个变量之前,之前的代码执行过这个变量的初始化,那么这个变量的使用就是合法的

局部变量:以函数调用为分界线,函数内声明的变量是不能在函数外使用的 。

全局变量:在函数外声明的全局变量,是可以在任意地方被访问到的。但是在函数内会默认创建一个同名的局部变量,我们可以使用 global 变量名 表示这个变量来自于全局变量,请不要创建局部变量而直接使用即可。

在定义模块时,所有的文件都不应该有全局变量和全局执行代码(即不在 def 和 class 中的代码),如果有,那么这些代码被import后将会被执行运行。

PYTHONPATH 用于指定 python库的搜索路径,在 pyCharm中会在开始前自动执行一段脚本,将当前的工程目录加入到搜索列表中去。

多版本python管理软件:多版本python管理工具:pyenv的使用

可以在交互式命令行中使用help(  查询的类名或者函数名  )来获得帮助信息

python内置函数:https://docs.python.org/3/library/functions.html

打包教程

python的import语句会将文件加载到当前文件中,在该文件首次加载时,会执行文件内部的顶层代码

import语句默认是调用 __import__(  “xxxx” )来加载模块,该函数返回一个集合名,之后需要以这个集合名为对象名,访问模块内的各种属性。

导入模块(包和模块是一样的)的两种形式:

import <模块名>:将会包含所有的xxx模块内的元素,在使用时,有命名空间限制,需要加上模块名 xxx 才能调用模块内的内容。(在最终文件使用时,推荐这种方式,避免大范围的命名冲突)

form  <模块名> import  <内容>:从模块中导入一个指定的部分到当前命名空间中。不需要再加上前缀,可以直接使用。(可能导致大范围的命名冲突,但是在 __init__.py 中建议使用中这种方式,以避免复杂的模块内命名空间

form <模块名> import * :可以完整的导入模块内的所有内容(函数、全局变量名等),并且不需要使用命名空间,但是非常容易引起冲突,

前面的内容均可以使用 as 为导入内容生成别名。例如 import CJFKit as CJFXXX.

建议在编写自己模块的时候,少用全局变量,避免冲突。

python的包组织形式是文件夹形式,文件夹名字即是包名。然后其内一定有一个 __init__.py 文件,其内包含所有的文件引用。

总之,使用 import引入包或者模块时,都需要加上前缀对象名才可以访问内容。

数据类型相关

None是一种类对象,是NoneType类型。如果没有return语句,函数执行完毕也会返回结果,只是结果为None。

Python标准数据类型:

Numbers(数字) :

int(有符号整型):位数是64位,最长能显示 [ -922 3372 0368 5477 5808, 922 3372 0368 5477 5808 ]。

long(长整型[也可以代表八进制和十六进制],后面加L):起码128位

float(浮点型)

complex(复数,表示为 a + bj 或者是 complex(a,b))

String(字符串) :

可以使用 单引号、双引号 和 三引号(期间包含的内容将不经过转码直接显示,可以直接包含换行、引号等内容) 表示字符串字面量。

支持下标操作,且支持负数下标。

使用 == 比较内容(注意在python3中 cmp函数被移除)

List(列表/序列) :

【】: 列表,即可变数组。支持下标操作,且支持负数下标。

打印时显示为 【 xx,xx,xx….】,且字符串类型会带上单引号。

Tuple(元组) :

():元组,即只读数组。支持下标操作,且支持负数下标。

打印时显示为 ( xx,xx,xx…. ),且字符串类型会带上单引号。

Dictionary(字典):

{ xxx:xxx, } : 字典。内容可变。键可以为任意的不可变类型,值则可任意类型。

dic.keys()会返回一个键列表,dic.values()会返回一个值列表。

打印时显示为 { xx:xx,xx…. },且字符串类型会带上单引号。

使用 in 检测是否存在key。比如 “xxxx” in dic ,返回值是个BOOL值。

Set(集合):

使用list或者是tuple来初始化set,会去重后生成set集合。表示为 {  1,2,3,………… } 这种类型

可以使用集合操作符:| 表示合集,& 表示交集,- 表示A相对于B的补集,^ 表示双方的差集

set要求传入的元素均为不可变对象,否则会直接报错。

不支持下标操作,会直接报错。

类型转换:

int(x [,base])将x转换为一个整数

long(x [,base] )

将x转换为一个长整数

float(x)

将x转换到一个浮点数

complex(real [,imag])

创建一个复数

str(x)

将对象 x 转换为字符串

repr(x)

将对象 x 转换为表达式字符串

eval(str)

用来计算在字符串中的有效Python表达式,并返回一个对象

tuple(s)

将序列 s 转换为一个元组

list(s)

将序列 s 转换为一个列表

set(s)

转换为可变集合

dict(d)

创建一个字典。d 必须是一个序列 (key,value)元组。

frozenset(s)

转换为不可变集合

chr(x)将一个整数转换为一个字符,只支持ASCII字符

unichr(x)

将一个整数转换为Unicode字符

ord(x)

将一个字符转换为它的整数值,只支持ASCII字符,直接以字符串的形式放入即可

hex(x)

将一个整数转换为一个十六进制字符串

oct(x)

将一个整数转换为一个八进制字符串

使用到变量[头下标:尾下标]可以截取 字符串、列表 和 元组的子元素。使用这种方式截取的子元素,其类型与原类型相同。且这是个半闭包(前包后不包)取值。

range(x1,x2):这也是一个半闭包,前包后不包。

Python不支持单字符类型,单字符也在Python也是作为一个字符串使用。

元组中只包含一个元素时,需要在元素后面添加逗号: tup1 = (50,)

变量的类型是可以变的,即一个变量在上一个语句指向一个str对象,接下来你还可以将它指向int对象。即所有指针都是通用的。(怀疑是存在一个通用的基类)

函数相关

默认参数

函数可以使用默认参数(必选参数在前,可选参数在后),例如 person ( name , age = 19 , city = “SH” )

可以选择性使用参数(即可跳过一定的中间可选参数),但对于选择性使用的参数需要强制指明变量名,类似于 city=”xcasca”。例如 person( “jack” , city = “BJ” ).

由于函数默认参数是采用静态对象的方式保存的,一旦被定义就维持着这一个对象,因此函数默认参数对象必须采用不可变对象

可变参数

使用 calc(*number ) 这样的形似就可以了一个可变参数函数。它可以接受多个参数传入,会将它们自动组合成为以number为变量名的tuple。

其允许传入0到多个参数

我们可以使用一个list或者tuple传入多个参数,那么需要在对象前加上*,表示解析为多个参数。

关键字参数

我们可以使用 person( name, age, **dic),这个 **kw就表示可变参数。然后调用时使用 person( “jack” ,  19 , “city” = “SH” , “job” = “fuck” ),这样的类型来调用,后面的两个参数会组装成字典 dic的元素,然后我们可以通过读取 dic来获取这些参数。

可以直接将一个字典 例如 extraDic ,使用 person( “jack” ,  19 , **extraDic ) 的方式传入。

命名关键字参数:(主要是为保证我们不传错参数)

定义类似于 person(name, age, *, city, job) ,正常情况下中间的 * 不可忽略。

如果 person(name, age, *args, city, job) ,存在可变参数,那么 * 必须忽略。注意,此时在可变参数之后的参数,都是命名关键字参数,需要使用键值对的形式才能赋值。

其在调用时,必须 使用 person( “jack” , 19 , city = “SH” , job = “enginner” ).必须指明命名关键字标志后面的参数的名字 和 值,否则会报错。

这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。例如:

deff1(a,b,c=0,*args,ddd,eee =19,**kw):

print(a);print(b);print(c)

print(“change”);forninargs:print(n);

print(“key”);print(ddd);print(eee)

print(“kw”);print(kw)

f1(1,2,3,4,5,6,7,8,9,ddd=1)

结果为:1、2、3、change、4、5、6、7、8、9、key、1、19、kw、{}

递归函数的尾递归优化

函数的堆栈层级是受限制的 ,一般肯定小于1000(在本机测试的结果是 998为极限)。

尾递归优化技术的条件:在return语句中,只返回本函数的调用,不包含任何的其他计算。(当然参数上可以计算),例如 return fact_iter( num -1 , num * product ) 这是符合尾递归优化的函数。但是 return n * fact( n -1 ) 这是不符合尾递归优化。

当符合尾递归优化时,编译器会优化调用,是其始终只占用一层调用堆栈。

并没有什么鸟用!!!因为Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题!

基础语法相关

变量不需要声明类型,直接使用即可,但Python依然是强类型语言,必要时需要类型转换语句。注意此处的转化并不是指指针上的转化,因为python中的指针是万能指针,可以指向任何对象。这里的转化是指对实际对象的转化工作,否则在运行时相应对象如果调用了它不存在的属性或者方法将会引起运行时错误。

print 自带了换行符,print后面接任何东西都会以string的形式打印出来

赋值语法:

a, b = b, a + b 表示  a = b ; b = a + b

s1,s2 = sss.split(“.”) :表示 将函数返回的list值,依次组装到等号左边的元素中去。注意,如果此处参数个数不相等,会产生运行时错误。

没有++ 或者 – – 语句,需要采用原始的 i = i+ 1来完成

语句后面不需要分号,if \ else \ for 语句后面需要 冒号

成员运算符:用于在容器类中包含判断包含元素。

in如果在指定的序列中找到值返回 True,否则返回 False。x 在 y 序列中 , 如果 x 在 y 序列中返回 True。

not in如果在指定的序列中没有找到值返回 True,否则返回 False。x 不在 y 序列中 , 如果 x 不在 y 序列中返回 True。

身份运算符:用于判断两个变量的存储单元是否一致。

isis是判断两个标识符是不是引用自一个对象x is y, 如果 id(x) 等于 id(y) ,is返回结果 1

is notis not是判断两个标识符是不是引用自不同对象x is not y, 如果 id(x) 不等于 id(y).is not返回结果 1

逻辑运算符:

andx and y布尔”与” – 如果 x 为 False,x and y 返回 False,否则它返回 y 的计算值。(a and b) 返回 20。

orx or y布尔”或” – 如果 x 是 True,它返回 True,否则它返回 y 的计算值。(a or b) 返回 10。

notnot x布尔”非” – 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。not(a and b) 返回 False

pass 语句pass是空语句,是为了保持程序结构的完整性。

for … else表示这样的意思,for 中的语句和普通的没有区别,else 中的语句会在循环正常执行结束(即for 不是通过 break 跳出而中断的)的情况下执行,while … else也是一样。

基本数学相关方法:http://www.runoob.com/python/python-numbers.html

随机数 与 随机方法

三角函数

类型转换

数学常量,使用 math库,math.pi 常量。

字符串相关所有操作符:

+字符串连接a + b 输出结果: HelloPython

*重复输出字符串a*2 输出结果:HelloHello

[]通过索引获取字符串中字符a[1] 输出结果e

[ : ]截取字符串中的一部分a[1:4] 输出结果ell

in成员运算符 – 如果字符串中包含给定的字符返回 TrueH in a输出结果 1

not in成员运算符 – 如果字符串中不包含给定的字符返回 TrueM not in a输出结果 1

r/R原始字符串 – 原始字符串:所有的字符串都是直接按照字面的意思来使用,没有转义特殊或不能打印的字符。 原始字符串除在字符串的第一个引号前加上字母”r”(可以大小写)以外,与普通字符串有着几乎完全相同的语法。print r’\n’输出 \n 和print R’\n’输出 \n

%格式字符串请看下一章节

字典值可以没有限制地取任何python对象,既可以是标准的对象,也可以是用户定义的,但键不行,键必须不可变,所以可以用数字,字符串或元组充当,所以用列表就不行。

类型判断:type( xxx )可以返回类对象,该思想与OC一致,且每种类对象都全局单例唯一。可以使用 type( xx ) is xClass 来判断(但不建议使用,已经不适用于类族的情况)。建议使用: isinstance( var , class  )来判断。

如果需要判断一个函数类型可以用这种方法。也可以使用:


或者是 使用 callable( xxx  ),如果返回true则表示可以,否则就表示不可以。(推荐这一种)

所有参数(自变量)在Python里都是按引用传递,所有变量在python中都是对象,python没有内置类型。如果你在函数里修改了参数,那么在调用这个函数的函数里,原始的参数也被改变了。

python支持可变参数传值,加了星号(*)的变量名会存放所有未命名的变量参数。选择不多传参数也可。可变参数变量在内部表现为元组形式使用

python中的 lamdba 可以持续的捕捉变量的数据,同时追踪它在 lamdba表达式之外是否发生变化,内部变量也同步发生变化。可以认为,其捕捉的变量和外部变量是同一个变量

python变量的作用域,只有全局和局部的区别。且局部变量只出现在函数调用内部,语句块不影响变量的可见性

Python会智能地猜测一个变量是局部的还是全局的,它假设任何在函数内赋值的变量都是局部的。因此,如果要给全局变量在一个函数里赋值,必须使用global语句。 global VarName的表达式会告诉Python, VarName是一个全局变量,这样Python就不会在局部命名空间里寻找这个变量了,否则结果就是会出现一个UnboundLocalError的错误。

使用__init__.py 形成包的组织形式。

File 对象方法: file对象提供了操作文件的一系列方法;OS 对象方法: 提供了处理文件及目录的一系列方法。

全局变量是全局可见的,且与定义位置无关。

自定义类相关

# 例子:

classEmployee:

‘所有员工的基类’   # 可以使用类的帮助信息可以通过ClassName.__doc__查看

empCount=0     # 算是静态变量

def__init__(self,name,salary):     #成员函数的第一个参数一定是 self

self.name=name     #成员变量不需要声明,直接使用即可

self.salary=salary

Employee.empCount+=1

在类定义中,与方法同级的变量是 static变量,归属于类对象。类对象最好使用类名.变量名去访问,直接使用 对象.变量名去访问,可能访问的是对象的动态属性。

对象的属性可以动态变更(不仅是类的实例变量的属性可以动态增删,连类对象的static变量都可以支持动态增删),使用如下方法,当我们使用 .属性访问时,其实默认内部调用的就是这些方法。目测在类内部维持有一个属性字典,以支持动态的增删属性

getattr(obj, name[, default]) : 访问对象的属性。如果不存在该属性,将会处罚exception。

hasattr(obj,name) : 检查是否存在一个属性。

setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。

delattr(obj, name) : 删除属性,删除后属性整个就不存在了。

如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加name和age属性。为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

classStudent(object):

__slots__ = (‘name’,’age’)# 用tuple定义允许绑定的属性名称

使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的

类的实例方法的参数,第一个一定是 self。

类的属性获取都应该先使用 hasattr 判断是否存在,然后再进行下一步操作。

使用 self.__class__ 或者 实例变量.__class__ 可以返回该实例变量的类对象。

类对象的内置属性:(注意是类对象的,而不是实例变量的,其实也是静态变量)

__dict__ : 类的属性(包含一个字典,由类的数据属性组成)【其中包含了所有的静态变量、方法(静态或非静态)、类的内置属性】

__doc__ :类的文档字符串

__name__: 类名

__module__: 类定义所在的模块(类的全名是’__main__.className’,如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)

__bases__ : 类的所有父类构成元素(包含了以个由所有父类组成的元组)

id( xxx  ) 可以返回一个对象的内存地址。可以用 id( xx ) == id( xxx ) 来判断是否是同一个对象。

类继承的写法以及特性:

实例:http://blog.chinaunix.net/uid-22920230-id-3070742.html

在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。

在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别于在类中调用普通函数时并不需要带上self参数(在类外调用的话,是都不需要的)。

Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。

类的方法可以可以动态的添加和删除

在类对象上添加方法:className.method_name = method; 。在添加完成后,所有的该类的实例对象都可以使用该方法。

在实例对象上添加:

需要先导入fromtypesimportMethodType

然后使用 instanceName.method_name = MethodType( method ,instanceName  )

这样的设置方法,该方法只有被设置的对象可以使用,其他对象不能不能使用。

动态设置属性方法

使用属性名作为设置和获取的方法名。

使用@propety修饰获取方法

使用@属性名.setter 来修饰设置方法

内部的实际存取变量或者运算还是由自己控制的。属性方法不会自动生成关联变量。

可以设置只读属性和只写属性。

示例:

定制化类:(内置方法的实现)

使用 __init__ 实现自己的定制化初始化方法。

使用__slots__ 赋值为元组,限制属性

实现 __len__ 函数可以在外部调用len()函数时,打印自己的长度。

打印相关:__str__ 方法,在print instance时被调用。__repr__ 在debug输出时被调用

成为Iterable对象:实现__iter__函数,返回相应的可迭代对象。如果是自己的话,那么再继续实现__next__函数,来支持 for-in循环。

支持下标和切片操作:实现 __getitem__ 函数,返回相应的list 或者 tuple。(要注意的是,切片有多种形式,需要覆盖完全才可,切片的类型是slice)

动态属性生成:__getattr__函数,可以支持返回变量和方法,如果是后者,那么外部需要加上调用式。

注意,此方法只有在无法找到合法属性时才会调用,且最好在尾部raise出异常

此方法与链式语法结合可以达到很好的效果

编程可调用对象:实现 __callable__ 函数,即可使对象可被调用

枚举类

使用 Enum生成简易枚举对象:

导入头文件:from enum import Enum

生成类对象:Month = Enum(‘Month’, (‘Jan’, ‘Feb’, ‘Mar’, ‘Apr’, ‘May’, ‘Jun’, ‘Jul’, ‘Aug’, ‘Sep’, ‘Oct’, ‘Nov’, ‘Dec’))

继承于Enum类对象,实现高度定制化枚举类:

导入头文件:from enum import Enum,unique

定义类:

@unique

classWeekday(Enum):

Sun =0# Sun的value被设定为0

Mon =1

Tue =2

Wed =3

Thu =4

Fri =5

Sat =6

高级语法特性

切边:即快速选取部分元素,这在上面有提到过,范围是个半闭包。

只能用于list和tuple

以下标作为截取范围标记

切边产生的元素类型与原类型一致

有多重表现形式:

【startIndex:endIndex】不包含末位

【:length】默认从0开始

【:】完整的赋值

【-a:-b】从倒数第b位开始,到倒数第a位,不包含第a位。注意,此时list末尾的依然是0开始,然后向左递减。注意,此时截取的list内元素顺序依然是之前的顺序,这个范围只决定截取范围。

【-a:】表示倒数第0位到倒数第a位,不包含倒数第a位。

【a:b:c】表示截取[a,b)范围元素,并且在这些元素中,每c个取一个,都取第一个,末位的分组也会被取一个。

迭代:符合迭代对象的,都可以使用 for-in遍历

包含 from collections import Iterable,使用 isinstance(‘abc’, Iterable) 可以判断一个对象是否是可迭代对象Iterable,如果是的话,那么就可以直接使用 for-in遍历。

常见的迭代器对象:range 、dict、list、tuple、set、str

其中dict的迭代方式最多:

for key in d : 默认遍历key

for value in d.values() :遍历value

for k, v in d.items() : 遍历key 和 value

使用 enumerate 实现下标循环: for i, value in enumerate([‘A’, ‘B’, ‘C’])

使用tuple可以获取多个变量:for x, y ,z in [(1, 1,1), (2, 4,6), (3, 9,27)]: ,但必须保证 变量个数与tuple内单个元素的length相同。

列表生成式

用于快速生成列表的表达式(且仅能用于list),基本规则为:【 生成表达式 for – in 语句  】

举例说明:[x * x for x in range(1, 11) if x % 2 == 0],这一段代码等价于 L = list() ; for x in range(1,11): if x % 2 == 0: L.append( x * x )

即 生成表达式最终会被放入 append 函数中,但只有符合前置条件,才能到达这个位置。

支持多种循环嵌套使用,且在中括号内的函数不需要使用 : 和 ;

生成器

是generator类型,该类型表示一个生成器对象,generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误,正统函数的返回值包含在StopIteration对象的value中。

这样可以有效的减少内存压力

generator也是一个迭代器对象,可以用于 for-in中,且这样遍历更为安全,不会产生越界错误。

generator有两种方式定义:

一种使用函数的方式来定义的,例如:

deffib(max):

n, a, b =0,0,1

whilen < max:

yieldb

a, b = b, a + b

n = n +1

return’done’

如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator,generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

一种是只要把一个列表生成式的[]改成(),就创建了一个generator

迭代器

主要分为两种类型:

一类是集合数据类型,如list、tuple、dict、set、str等,这些都是Iterable对象,可以通过iter()函数转化一个Iterator对象。

一类是generator,包括生成器和带yield的generator function,这些本身就是Iterator对象;

Iterable类型:可以用于for循环

Iterator类型:不止可以用于for循环,还可以用你next 获取下一个元素。

Python的for-in循环本质上就是通过不断调用next()函数实现的,在遇到exception时break。

函数式编程

函数式编程简介:https://github.com/justinyhuang/Functional-Programming-For-The-Rest-of-Us-Cn/blob/master/FunctionalProgrammingForTheRestOfUs.cn.md

函数式编程的特点

其与数学公式是相对应的,特别是当我们使用generator的时候,他可能就是一组符合公式的无穷数组。

它是惰性求值的,我们不需要担心内存问题。

它的函数也是对象,是一等公民。我们可以把函数作为对象传入或输出,更可以结合 lambda表达式创造可变函数返回对象:(例如)

def_not_divisible(n):

returnlambdax: x % n >0

函数本身也是变量,即变量可以指向函数。函数名也是变量,一旦被重新赋值,就不再指向原来的函数,而是指向新的变量。其类型为

一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数

常用系统高阶函数

map( func, iterable )

将func 应用于iterable中的每一个元素,并返回一个 iterator。

注意,由于返回的是 iterator,因此是惰性求值的,如果我们希望等到一个已运算完成的数据集合,需要使用 list( iterator ),即将 iterator list化。

reduce( func, iterable ):

表示 func( x,y ) ,其中x是上次运算的结果,y为下一个元素,首次时x,y分别为前两个元素。这是一种归纳求值的操作。

注意,reduce() 需要包含 from functools import reduce ,方能使用。

注意,由于返回的是 iterator,因此是惰性求值的,如果我们希望等到一个已运算完成的数据集合,需要使用 list( iterator ),即将 iterator list化。

filter( func , iterable ):

filter()函数用于过滤序列

filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素.

注意,由于返回的是 iterator,因此是惰性求值的,如果我们希望等到一个已运算完成的数据集合,需要使用 list( iterator ),即将 iterator list化。

请参见:http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001431821084171d2e0f22e7cc24305ae03aa0214d0ef29000里面关于素数筛选的例子非常好的诠释了函数式编程的特点和优势,虽然我还不明白它编译器要怎么做,但确实是很不一样的编程思路。

sorted(….):

这是一个排序函数,且支持传入预操作函数和排序顺序。

有两类sorted函数:(在python3中 cmp函数被移除)

sorted(iterable, key=None, reverse=False)

L.sort( key=None, reverse=False)

其中的key可以使用 lambda表达式: 比如 key= lambda x:x[“xxx”] ,即 x表示一个元素, 冒号后面返回参与比较的元素

iterableb表示迭代器对象,key函数确定作为排序依据的key值,将会在排序前作用于每一个元素并取得这个元素的key值,然后根据这个key值进行排序,reverse表示是否反序。

需要注意的是,sorted函数不会影响list中元素的值,只会影响其排序关系。

内部函数与返回函数

通常返回函数都是内部函数,这些内部函数可以捕获外部变量被自己使用。这些特性称为闭包。

返回的函数并没有立刻执行,而是直到调用了f()才执行

这些捕获的变量通常会指向同一块内存地址,因此会导致外部变量变化时,捕获变量也一起变化。

返回函数不要引用任何循环变量,或者后续会发生变化的变量。否则会导致函数难以维护以及内存泄露。

如果一定要引用循环变量:方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

匿名函数

python对lambda提供有限的支持。

匿名函数有个限制,就是只能有一句表达式,不用写return,返回值就是该表达式的结果

可以把匿名函数作为返回值返回

形式:lambda 【paramter】: 【表达式】

装饰器

这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)

wrapper()函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。

decorator()函数的参数定义是(func),不能附加其他参数,这是规范。如果希望传入外界变量给装饰器函数使用,那么就需要外界再套一个函数,这样避免循环引用

装饰器函数根据是否需要捕获外界变量分为两种:

不捕获外界变量:

捕获外界变量:它所能捕获的参数仅限于log函数的传入参数。这里需要使用三层的原因,是由于@的展开式导致的,可以看一下下面关于@展开格式的论述。(注意,@展开式是固定的)

函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的’now’变成了’wrapper’。因为返回的那个wrapper()函数名字 就是’wrapper’, 所以,需要把原始函 数的__name__等属 性复制到 wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

这句话的本质其实就是def now ….; now = log(now)。其实就是对@xxx做了一次调用。如果是@log(“asa”)则变成 now = log(“asa”)( now ) 的调用形式。最后结果一定要是返回 wrapper函数。

更多内容可以参考:pycharm/python3/LearnDecorator.

装饰器的用法:

先在前面定义相应的装饰器函数,根据是否需要使用额外的外部变量,采取上面两张写法中的一种。

使用@装饰器函数名字,如果有参数的话,在此处还有传入参数。

紧接着上面这行代码,定义需要使用到函数

为装饰器函数改名:只需记住在定义wrapper()的前面加上@functools.wraps(func)即可。需要预先

importfunctools.

偏函数:

当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。

int2 = functools.partial(int, base=2)  — int2(‘1000000’) => int2(‘1000000’, base=2)

被设置的默认参数依然可以继续被覆盖

代码调试

if __name__==’__main__’: test()   。 当我们在命令行运行hello模块文件时,Python解释器把一个特殊变量__name__置为__main__,而如果在其他地方导入该hello模块时,if判断将失败,因此,这种if测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试。

命名规范

变量命名:

全局变量:一定要在前面加上”G_” 前缀

不可变变量:一定要加上 “C_” 前缀

全局不可变变量:加上 “GC_” 前缀

临时变量:可以随意命名,采用驼峰命名法

函数命名:

本地函数:

外部可见引用函数:

    原文作者:健康哥哥
    原文地址: https://www.jianshu.com/p/c17307cc2427
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞