Python自学笔记D6
函数式编程、模块与面向对象编程
前两天事比较多,今天得专心补一补!在2021年6月之前一定要成为一个合格的自动化测试人员!
一、排序算法sorted
sorted可以非常方便地对字符串和dic进行排序,sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:
sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower,reverse = True)
['Zoo', 'Credit', 'bob', 'about']#忽略大小写,按ascii反转排序
作业,按照名字排序和按照成绩从大到小排序。
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
print(sorted(L,key= lambda x: x[0]))#按照名字
print(sorted(L,key= lambda x: x[1],reverse=True))#按照分数倒序
二、返回函数
即return中返回的是函数!
def lazy_sum(*args):
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用。
作业:返回一个计数器
def createCounter():
cnt = [0]#不能使用循环变量,因此使用数组
def counter():
cnt[0] = cnt[0] + 1
return cnt[0]
return counter
#以下为闭包错误写法:内部函数改变外部变量!!
i = 0
def counter():
i = i + 1
return i
return counter
三、匿名函数lambda
f = lambda x:x*x
#等同于
def f(x):
return x*x
匿名函数的限制是只能写一个表达式,不能有return,好处就是没有名字不起冲突。
四、装饰函数decorate
假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
因此!我们需要写一个装饰函数,再写一个内层函数(不改变原先的值)
import functools
def log(func):
@functools.wraps(func)#修改签名以防出错
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2015-3-25')
now()
call now():
2015-3-25
#第二种:需要多一个返回参数的情况,即三层嵌套
import functools
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('execute')
def now():
print('2015-3-25')
now()
execute now():
2015-3-25
作业即应用:打印任意函数的执行时间!很经典
import time, functools
def metric(fn):
@functools.wraps(fn)
def wrapper(*args,**kwargs):
start = time.time()
result = fn(*args,**kwargs)
print('%s executed in %s ms' % (fn.__name__,time.time() - start))
return result
return wrapperr
#以下为检测
@metric
def fast(x, y):
time.sleep(0.0012)
return x + y;
@metric
def slow(x, y, z):
time.sleep(0.1234)
return x * y * z;
f = fast(11, 22)
s = slow(11, 22, 33)
if f != 33:
print('测试失败!')
elif s != 7986:
print('测试失败!')
五、偏函数
functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。(简化参数的目的)
import functools
int2 = functools.partial(int, base=2)
int2('1001')
#实际相当于
kw = { 'base': 2 }
int('10010', **kw)
二、模块
为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。在Python中,一个.py文件就称之为一个模块
为了避免模块名字重复,可以通过包来组织模块,只要顶层包名不冲突,则包下面的模块名也不会冲突,如mycompany.web.www等,不过myconpany.web指的是web中的_init_.py(必须有的一个初始文件)
不过自己命名模块的时候注意不能与系统模块名字冲突!!
完整的模块:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' 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__':#重要!如果B调用A则A可能不能使用
test()
作用域
有些函数和变量不希望在模块外被应用,因此可以设定作用域。
_xxx_为有特殊含义的变量,可以被直接饮用,而_xx是不应该被直接引用的,但也无法绝对限制!
def _private_1(name):
return 'Hello, %s' % name
def _private_2(name):
return 'Hi, %s' % name
def greeting(name):
if len(name) > 3:
return _private_1(name)
else:
return _private_2(name)
第三方模块安装
使用包管理工具pip管理,安装了Anaconda进行管理。
面向对象编程
OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。
而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
对象数据类型即类的概念!
#面向对象!首先创建对象,接着让对象自己吧数据打印
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))
#面向过程
std1 = { 'name': 'Michael', 'score': 98 }
std2 = { 'name': 'Bob', 'score': 81 }
def print_score(std):
print('%s: %s' % (std['name'], std['score']))
三大特点:继承、封装和多态!
类和实例
面向对象最重要的两个概念!
类是抽象的模板,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。
class Student(object): class之后是类名,括号中是继承自类。
bart = Student()#创建实例
bart.name = 'Bart Simpson'#可以*给实例绑定属性
class Student(object):
#提前在类中绑定属性!(只能使用_init_)
def __init__(self, name, score):#使用self表示本身!
self.name = name
self.score = score
#再封装一个打印成绩
def print_score(self):
print('%s: %s' % (self.name, self.score))
和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例
变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同!!
访问限制
如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名(属性)如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,不过使用某些方法也可以访问到。
class Student(object):
...
def get_name(self):
return self.__name
def set_score(self, score):
if 0 <= score <= 100:
self.__score = score
else:
raise ValueError('bad score')
一个下划线开头则不强制规定,但是最好不要改。
继承和多态
在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base 、Super class)。
多态性:比如动物的子类猫,猫的实例加菲猫,数据类型既是猫也是动物!
多态性的好处:任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行。
静态语言与动态语言区别:
对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。
对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了:
获取对象信息
可以使用type判断类型!
>>> type(abs)
<class 'builtin_function_or_method'>
>>> type(a)
<class '__main__.Animal'>
而对class的继承关系来说,可以使用isinstance(),并且也可以判断类型!
a = cat()
>>>isinstance(a,Animal)
True
>>> isinstance(abs)
<class 'builtin_function_or_method'>
>>>isinstance(a,(Animal,jiafei))#或者
True
dir()返回所有属性和方法。
通过getattr、hasattr和setattr操作对象状态
>>> hasattr(obj, 'x') # 有属性'x'吗?
True
>>> obj.x
9
>>> hasattr(obj, 'y') # 有属性'y'吗?
False
>>> setattr(obj, 'y', 19) # 设置一个属性'y'
>>> hasattr(obj, 'y') # 有属性'y'吗?
True
>>> getattr(obj, 'y',404) # 获取属性'y',不存在则返回404
19
>>> obj.y # 获取属性'y'
19
def readImage(fp):
if hasattr(fp, 'read'):
return readData(fp)
return None