学习笔记

0208-函数-对象编程

Posted on 2022-02-08,9 min read
封面图

返回函数

类似js的闭包;
例子1:

def count():
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
    return fs

计数器:
fn()函数内部加一个nonlocal x的声明, 解释器把fn()的x看作外层函数的局部变量,它已经被初始化了,可以正确计算x+1.

def createCounter():
    x = 0
    def counter():
        nonlocal x  #去掉会报错
        x = x +1
        return x
    return counter
f = createCounter()
print(f()) # 1
print(f()) # 2
print(f()) # 3
print(f()) # 4

匿名函数

lambda 定义了匿名函数
过滤偶数:
filter 中的函数参数没有()参数

# -*- coding: utf-8 -*-
def is_odd(n):
    return n % 2 == 1
L = list(filter(is_odd, range(1, 20))) #is_odd不需要写参数
#通过匿名函数
L = list(filter(lambda x : x % 2 == 1, range(1, 20)))

装饰器

wrapper() 函数的参数定义是(*args, **kw),因此,wrapper()函数可以接受任意参数的调用。
wrapper.__name__ = func.__name__等同于Python内置的@functools.wraps(func)
设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:

# -*- coding: utf-8 -*-
import time, functools

def metric(fn):
    @functools.wraps(fn)
    def wrapper(*args,**kw):
        startTime = time.time()
        result = fn(*args,**kw)
        endTime = time.time()
        excuteTime = 1000 * (endTime- startTime)
        print('%s executed in %s ms' % (fn.__name__, excuteTime))
        return result
    return wrapper

# 测试
@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)
##输出
fast executed in 1.293182373046875 ms
slow executed in 123.56829643249512 ms

带参数的decorator

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

偏函数

int() 函数提供 base 参数,默认值为 10。如果传入 base 参数,就可以做N进制的转换:

>>> int('12345', base=8)
5349
>>> int('12345', 16)
74565

利用偏函数固定参数:
functools.partial 固定函数的某个参数, 实现简化函数调用

>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
>>> int2('1010101')
85

模块

以内建的 sys 模块为例:

#!/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__':
    test()

作用域
类似_xxx__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc__abc等;
Python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量。
private函数的作用示例:

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)

面向对象编程

类和实例

和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。
定义类:

class Student(object):

    def __init__(self, name, score):
        self.name = name
        self.score = score

(object),表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。

>>> bart = Student('Bart Simpson', 59)
>>> bart.name
'Bart Simpson'
>>> bart.score
59

数据封装
Student类的内部定义访问数据的函数,这样,就把“数据”给封装起来了。这些封装数据的函数是和Student类本身是关联起来的,我们称之为类的方法:

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))

除了self不用传递,其他参数正常传入:

>>> bart.print_score()
Bart Simpson: 59

访问限制

通过私有变量private实现: __name, __score
双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量:

>>> bart._Student__name
'Bart Simpson'

继承与多态

多态: 子类方法与父类重合时会覆盖父类的方法,即优先调用子类的方法.

class Animal(object):
    def run(self):
        print('Animal is running...')
		
class Dog(Animal):
    pass
class Cat(Animal):
    pass
#调用
dog = Dog()
dog.run()
cat = Cat()
cat.run()
#结果
Animal is running...
Animal is running...
#子类方法:
class Dog(Animal):

    def run(self):
        print('Dog is running...')

class Cat(Animal):

    def run(self):
        print('Cat is running...')
#再次调用结果:
Dog is running...
Cat is running...

def run_twice(animal):
    animal.run()
    animal.run()
>>> run_twice(Animal())
Animal is running...
Animal is running...
>>> run_twice(Dog())
Dog is running...
Dog is running...

Python的动态特点, 不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。
不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了.

获取对象信息

type()
isinstance()
dir():
获得一个str对象的所有属性和方法:

>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']

getattr()setattr()hasattr(),可以直接操作一个对象的状态:

>>> class MyObject(object):
...     def __init__(self):
...         self.x = 9
...     def power(self):
...         return self.x * self.x
...
>>> obj = MyObject()
#操作
>>> 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') # 获取属性'y'
19
>>> obj.y # 获取属性'y'
19
#传入一个default参数
>>> getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404
404
#获得对象的方法:
>>> hasattr(obj, 'power') # 有属性'power'吗?
True
>>> getattr(obj, 'power') # 获取属性'power'
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn
>>> fn # fn指向obj.power
<bound method MyObject.power of <__main__.MyObject object at 0x10077a6a0>>
>>> fn() # 调用fn()与调用obj.power()是一样的
81

实例属性与类属性

创建类的属性:

class Student(object):
    name = 'Student'

测试:

>>> class Student(object):
...     name = 'Student'
...
>>> s = Student() # 创建实例s
>>> print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
Student
>>> print(Student.name) # 打印类的name属性
Student
>>> s.name = 'Michael' # 给实例绑定name属性
>>> print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
Michael
>>> print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问
Student
>>> del s.name # 如果删除实例的name属性
>>> print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了
Student

为了统计学生人数,可以给Student类增加一个类属性,每创建一个实例,该属性自动增加:

class Student(object):
    
    count = 0

    def __init__(self, name):
        self.name = name
        Student.count += 1
        self.count = Student.count
#测试
# 测试:
if Student.count != 0:
    print('测试失败!')
else:
    bart = Student('Bart')
    if Student.count != 1:
        print('测试失败!')
    else:
        lisa = Student('Bart')
        if Student.count != 2:
            print('测试失败!')
        else:
            print('Students:', Student.count)
            print('测试通过!')

下一篇: 0207-高级特性-高级函数→

loading...