Python进阶学习笔记(一)

Python进阶学习笔记(一)

一、函数式编程

函数式编程是一种抽象计算的编程模式。

1. 函数式编程的特点

1.1 把计算视为函数,而非指令;
1.2 纯函数式编程:不需要变量,没有副作用,测试简单;
1.3 支持高阶函数,代码简洁。

2. Python支持的函数式编程

2.1 不是纯函数式编程:允许有变量;
2.2 支持高阶函数:函数可作为变量传入;
2.3 支持闭包:有了闭包就能返回函数;
2.4 有限度的支持匿名函数。

3. 不同语言的抽象层次

Python进阶学习笔记(一)_第1张图片

4. 高阶函数

4.1 定义高阶函数

  • 变量可以指向函数
  • 函数的参数可以接收变量
  • 一个函数可以接收另一个函数作为参数
  • 能接收函数作为参数的函数就是高阶函数
# 定义一个高阶函数
def add(x, y, f):
    return f(x) + f(y)

# 当我们传入abs函数时
add(-5, 9, abs)

4.2 Python中内置的高阶函数

  • map(f,L):接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回;
def f(x):
    return x*x
print map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
  • reduce(f,L):reduce()函数接收的参数和map()类似,一个函数f,一个list,但行为和 map()不同,reduce()传入的函数f必须接收两个参数,reduce()对list的每个元素反复调用函数f,并返回最终结果值。
# 求两数之和
def f(x, y):
    return x + y
# reduce()函数则是返回list中元素的和
# 求和过程:
# 1 + 3 = 4
# 4 + 5 = 9
# 9 + 7  = 16
# 16 + 9 = 25 
# 最终返回值就是25
reduce(f, [1, 3, 5, 7, 9])
  • reduce(f,L,n):在上述reduce基础上,n代表初始值,在n的基础上进行f计算。
# 求两数之和
def f(x, y):
    return x + y
# 返回值为125
reduce(f, [1, 3, 5, 7, 9], 100)
  • filter(f,L):filter()函数接收一个函数f和一个list,这个函数f的作用是对每个元素进行判断,返回True或False,filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。
# 判断是否是奇数
def is_odd(x):
    return x % 2 == 1
# 返回list中的奇数
filter(is_odd, [1, 4, 6, 7, 9, 12, 17])
  • sorted(L):将list中的元素从小到大排序。
  • sorted(L,f):将list中的元素按照f中的排序方法排序。比较函数f的定义是,传入两个待比较的元素 x, y,如果 x 应该排在 y 的前面,返回 -1,如果 x 应该排在 y 的后面,返回 1。如果 x 和 y 相等,返回 0。
def reversed_cmp(x, y):
    if x > y:
        return -1
    if x < y:
        return 1
    return 0
sorted([36, 5, 12, 9, 21], reversed_cmp)

5. 返回函数

Python的函数不但可以返回int、str、list、dict等数据类型,还可以返回函数。

def f():
    print 'call f()...'
    # 定义函数g:
    def g():
        print 'call g()...'
    # 返回函数g:
    return g

x = f()   # 调用f()   得到结果:call f()...
x   # 变量x是f()返回的函数:
x()   # x指向函数,因此可以调用,得到结果:call g()...   
      #调用x()就是执行g()函数定义的代码

返回函数可以把一些计算延迟执行,我们可以在后续代码里,决定到底要不要调用该函数。

6. 闭包

6.1 定义闭包

内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)。

6.2 闭包的特点

返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。

def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()
# 执行结果为:f1()-->9,f2()-->9,f3()-->9
# 就是因为在内层函数中引用了循环中的参数。

7. 匿名函数

在Python中,对匿名函数提供了有限支持。关键字lambda 表示匿名函数,冒号前面的 x 表示函数参数。匿名函数有个限制,就是只能有一个表达式,不写return,返回值就是该表达式的结果。

使用匿名函数,可以不必定义函数名,直接创建一个函数对象,很多时候可以简化代码:

sorted([1, 3, 9, 5, 0], lambda x,y: -cmp(x,y))
# 得到的结果:[9, 5, 3, 1, 0]

返回函数的时候,也可以返回匿名函数:

myabs = lambda x: -x if x < 0 else x 
myabs(-1)
# 得到的结果:1

8. decorator装饰器

8.1 什么是装饰器

定义了一个函数,想在运行时动态增加功能,且不改动函数本身代码时,就需要装饰器。

# 如现在想在执行下面的函数时打印log,怎么办
def f1(x):
    return x*2
def f2(x):
    return x*x

# 方案一:直接在函数中添加log
def f1(x):
    print 'call f1()...'
    return x*2
def f2(x):
    print 'call f2()...'
    return x*x

# 方案二:使用高阶函数
def new_fn(f):
    def fn(x):
        print 'call '+f.__name__+'()...'
        return f(x)
    return fn

f1 = new_fn(f1)
f2 = new_fn(f2)
f1()
f2()

# 方案三:使用装饰器
def new_fn(f):
    def fn(x):
        print 'call '+f.__name__+'()...'
        return f(x)
    return fn

@new_fn
def f1(x):
    return x*2
@new_fn
def f2(x):
    return x*x

根据上面的示例,可以看出:
- 方案一是最不可取的,它需要我们对每个需要打印log的函数修改代码
- 方案二、方案三原理相同,但是方案三则让我们的代码更简洁明了
- Python内置的@语法就是为了简化装饰器调用的。
- 装饰器的作用:可以极大地简化代码,避免每个函数编写重复性代码

8.2 无参数装饰器

这里的无参数指的是装饰器无参数,而不是被装饰的函数无参数。

import time

# 获取函数的执行时间
def performance(f):
    # f函数的参数个数及类型不确定,所以使用可变参数,否则容易报错
    # *args表示:无key可变参数
    # **kw表示:有key可变参数
    def perf_fn(*args,**kw):
        t1 = time.time()
        r = f(*args,**kw)
        t2 = time.time()
        print 'call ' + f.__name__ + '() in '+str(t2-t1)
        return r
    return perf_fn

# 获取n的阶乘
@performance
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))

print factorial(10)

上面的示例就是一个无参数装饰器,它的作用是输出函数的执行时间。
*args表示:无key可变参数
**kw表示:有key可变参数

8.3 有参数装饰器

同样,这里的有参数指的是装饰器有参数,而不是被装饰的函数有参数。

import time

def performance(unit):
    def perf1(f):
        def perf_fn(*args,**kw):
            t1 = time.time()
            r = f(*args,**kw)
            t2 = time.time()
            t = (t2-t1)
            if unit == 'ms':
                t = t * 1000
            print 'call '+f.__name__+'() in '+ str(t)
            return r
        return perf_fn
    return perf1

@performance('ms')
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))

print factorial(10)

上面的示例就是带有参数的装饰器,它可以根据参数,打印时间是毫秒还是秒。

8.4 完善装饰器

def log(f):
    def wrapper(*args, **kw):
        print 'call...'
        return f(*args, **kw)
    return wrapper
@log
def f2(x):
    pass
print f2.__name__

# 输出结果:wrapper

上面的示例,我们输出函数的名称,但是,输出的结果却不是我们的预期结果。那么我们要怎么才能得到我们的预期结果呢?
Python中有内置的functools可以用来自动化完成这个“复制”的任务。

import functools
def log(f):
    @functools.wraps(f)
    def wrapper(*args, **kw):
        print 'call...'
        return f(*args, **kw)
    return wrapper
@log
def f2(x):
    pass
print f2.__name__
# 输出结果:f2

但是,由于我们把原函数签名改成了(*args,**kw),因此,无法获得原函数的原始参数信息。即便我们采用固定参数来装饰只有一个参数的函数。

8.5 偏函数

functools.partial就是帮助我们创建一个偏函数的,它可以把一个参数多的函数变成一个参数少的新函数,少的参数需要在创建时指定默认值,这样,新函数调用的难度就降低了。

import functools
int2 = functools.partial(int, base=2) # 默认转成二进制数
int8 = functools.partial(int, base=8) # 默认转成八进制数

二、模块和包

项目中,我们不能把所有的代码把写在一个py文件中,这样会导致代码可读性、可维护性较差,所以我们会根据功能等方式将代码写入多个py文件中,这样,每个py文件就是一个模块,py文件名就是模块名;如果项目由多个人协作完成,则有可能会导致模块名会重复,则需要分包,不同的包中,即使模块名重复也没关系。计算机如何区分包和普通文件夹目录呢?每层包下都有一个init.py文件,即使是空文件。

1. 导入模块

1.1 导入Python自带模块

# test.py           # 自身模块名test
import math         # 导入math模块
print math.pow(2,10)# 调用math模块的公共方法

1.2 导入不同包中的模块

# test1.py          # 自身模块名test
import p1.util      # 导入p1包中util模块
import p2.util      # 导入p2包中util模块

print p1.util.f1(10)# 引用p1包中util模块中f1
print p2.util.f2(10)# 引用p2包中util模块中f2

# 即使util模块重名,但是在不同的包中,则不冲突

1.3 导入模块中的部分函数

from math import pow,sin
print pow(2,10)
print sin(3.14)

1.4 给不同模块中同名函数起别名

from math import log
# logging的log现在变成了logger
from logging import log as logger  

print log(10)   # 调用的是math的log

# 调用的是logging的log
logger(10, 'import from logging')  

1.5 动态导入模块

try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO

上述代码先尝试从cStringIO导入,如果失败了(比如cStringIO没有被安装),再尝试从StringIO导入。这样,如果cStringIO模块存在,则我们将获得更快的运行速度,如果cStringIO不存在,则顶多代码运行速度会变慢,但不会影响代码的正常执行。
try 的作用是捕获错误,并在捕获到指定错误时执行 except 语句。

三、面向对象编程

面向对象编程是一种程序设计范式,把程序看做不同对象的相互调用,面向对象编程中,世界万物皆是对象。

1. 面向对象编程的基本思想

Python进阶学习笔记(一)_第2张图片
如图,“人”就是一个类,它是一个抽象类型,黑人、白人、男人、女人都是“人”这个类中的。
“小明”、“小军”、“小红”则是“人”这个类的具体实例,他们带有自己的属性,如:性别、肤色、姓名、年龄等。每个实例都是一个独立的对象。

所以,面向对象编程的基本思想是:将具有相同属性的抽象成一个类,而这个类的对象就是这个类的实例。

2. 定义类与创建实例

# 创建Person类
class Person(object):
    pass
# 实例化出两个对象
xiaoming = Person()
xiaohong = Person()

按照 Python 的编程习惯,类名以大写字母开头,紧接着是(object),表示该类是从哪个类继承下来的。类的继承将在后面讲解,现在我们只需要简单地从object类继承。

3. 属性

Python中的属性分为实例属性与类属性。实例属性则是每个实例的唯一属性,类属性是类中唯一的属性。

3.1 实例属性

# 创建两个实例,并对两个实例添加各自的属性
xiaoming = Person()
xiaohong = Person()

xiaoming.name = 'Xiao Ming'
xiaoming.gender = 'Male'
xiaoming.birth = '1990-1-1'

xiaohong.name = 'Xiao Hong'
xiaohong.school = 'No. 1 High School'
xiaohong.grade = 2

上面的代码,可以看出每个实例的属性都是这个实例的唯一的。但是,这样的方式添加属性过于麻烦,那么我们就在初始化实例时,添加各自的属性:

class Person(object):
    def __init__(self, name, gender, birth):
        self.name = name
        self.gender = gender
        self.birth = birth

xiaoming = Person('Xiao Ming', 'Male', '1991-1-1')
xiaohong = Person('Xiao Hong', 'Female', '1992-2-2')

在定义Person类时,可以为Person类添加一个特殊的__init__()方法,当创建实例时,__init__()方法被自动调用,我们就能在此为每个实例都统一添加属性。__init__()方法中,第一个参数必须是self,它表示这个实例本身。

3.2 类属性

class Person(object):
    count = 0
    def __init__(self,name):
        self.name = name
        Person.count = Person.count + 1 

p1 = Person('Bob')
print Person.count

p2 = Person('Alice')
print Person.count

p3 = Person('Tim')
print Person.count

上面的代码给 Person 类添加一个类属性count,每创建一个实例,count属性就加1,这样就可以统计出一共创建了多少个 Person 的实例。

3.3 类属性名与实例属性名冲突

class Person(object):
    address = 'Earth'
    def __init__(self, name):
        self.name = name

p1 = Person('Bob')
p2 = Person('Alice')

print 'Person.address = ' + Person.address

p1.address = 'China'
print 'p1.address = ' + p1.address

print 'Person.address = ' + Person.address
print 'p2.address = ' + p2.address

# 结果如下:
Person.address = Earth
p1.address = China
Person.address = Earth
p2.address = Earth

由上面的执行结果可以看出:当实例属性和类属性重名时,实例属性优先级高,它将屏蔽掉对类属性的访问;当实例属性没有,而类属性有时,则返回类属性。所以,不要用实例修改类属性,它实际上并没有修改类属性,而是给实例绑定了一个实例属性。

3.4 属性的访问限制

我们可以给一个实例绑定很多属性,如果有些属性不希望被外部访问到怎么办?Python对属性权限的控制是通过属性名来实现的,如果一个属性由双下划线开头(__),该属性就无法被外部访问。

class Person(object):
    def __init__(self, name):
        self.name = name
        self._title = 'Mr'
        self.__job = 'Student'
p = Person('Bob')
print p.name
print p._title
print p.__job

# 输出结果:
# => Bob
# => Mr
# => Error
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: 'Person' object has no attribute '__job'

由此可以看出,当我们将属性设置权限后,再用实例访问就会报错,被设置权限的属性只能在类的内部直接访问。类属性,也同样如此。
但是,如果一个属性以”xxx“的形式定义,那它又可以被外部访问了,以”xxx“定义的属性在Python的类中被称为特殊属性,有很多预定义的特殊属性可以使用,通常我们不要把普通属性用”xxx“定义。

4. 方法

一个私有属性就是以__开头的属性,无法被外部访问,那这些属性定义有什么用?虽然私有属性无法从外部访问,但是,从类的内部是可以访问的。除了可以定义属性外,还可以定义方法。同样,方法也有实例方法与类方法。

4.1 实例方法

实例的方法就是在类中定义的函数,它的第一个参数永远是self,指向调用该方法的实例本身,其他参数和一个普通函数是完全一样的。

class Person(object):

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

    def get_name(self):
        return self.__name

p1 = Person('Bob')
print p1.get_name()  # self不需要显式传入

#输出结果:
Bob

get_name(self) 就是一个实例方法,它的第一个参数是self。__init__(self,name)其实也可看做是一个特殊的实例方法。
在实例方法内部,可以访问所有实例属性,这样,如果外部需要访问私有属性,可以通过方法调用获得,这种数据封装的形式除了能保护内部数据一致性外,还可以简化外部调用的难度。

4.2 类方法

class Person(object):
    __count = 0

    @classmethod
    def how_many(cls):
        return cls.__count

    def __init__(self,name):
        self.name = name
        Person.__count += 1

print Person.how_many()
p1 = Person('Bob')
print Person.how_many()

#输出结果:
0
1

在class中定义的全部是实例方法,实例方法第一个参数self是实例本身。而类方法则需要通过标记一个 @classmethod,该方法将绑定到Person类上,而非类的实例。类方法的第一个参数将传入类本身,通常将参数名命名为cls,上面的cls.count实际上相当于Person.count。因为是在类上调用,而非实例上调用,因此类方法无法获得任何实例变量,只能获得类的引用。

4.3 方法也是属性

我们在 class 中定义的实例方法其实也是属性,它实际上是一个函数对象。

class Person(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def get_grade(self):
        return 'A'

p1 = Person('Bob', 90)
print p1.get_grade
print p1.get_grade()

# 输出结果:
0x109e58510>>
A

也就是说,p1.get_grade返回的是一个函数对象,但这个函数是一个绑定到实例的函数,p1.get_grade() 才是方法调用。

因为方法也是一个属性,所以,它也可以动态地添加到实例上,只是需要用types.MethodType() 把一个函数变为一个方法。

import types
def fn_get_grade(self):
    if self.score >= 80:
        return 'A'
    if self.score >= 60:
        return 'B'
    return 'C'

class Person(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score

p1 = Person('Bob', 90)
p1.get_grade = types.MethodType(fn_get_grade, p1, Person)
print p1.get_grade()
p2 = Person('Alice', 65)
print p2.get_grade()

# 输出结果:
A
ERROR: AttributeError: 'Person' object has no attribute 'get_grade'
# 因为p2实例并没有绑定get_grade

给一个实例动态添加方法并不常见,直接在class中定义要更直观。

我们可以将函数动态添加到实例上,那么函数与方法有什么区别呢?
Python中Function(函数)和methon(方法)

四、类的继承

相信有过面向对象语言开发经验的同学对类的继承已经不陌生了,类的继承是面向对象编程的特性之一,子类可以获得父类的属性与方法,并且可以添加自己特有的属性和方法,那么Python中类的继承有没有什么不一样的地方呢?当然有,那就是Python中可以多继承,也就是子类可以继承多个父类,多么的不可思议,“孩子”居然可以有几个“父亲”!!!接下来,我们就一一来说。

1. 单继承

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

class Teacher(Person):

    def __init__(self, name, gender, course):
        super(Teacher,self).__init__(name,gender)
        self.course = course

t = Teacher('Alice', 'Female', 'English')
print t.name
print t.course

父类Person中有name和gender属性,子类Teacher继承Person,并添加了course属性。在子类初始化实例时,一定要调用super.__init__()方法,否则会找不到父类中的属性。当然,如果你不需要父类的属性,可以不调用。

isinstance(s,cls):判断类型,即判断s是否属于cls类,是:返回True;否:返回False。

class Person(object):

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

class Student(Person):

    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

class Teacher(Person):

    def __init__(self, name, gender, course):
        super(Teacher, self).__init__(name, gender)
        self.course = course

t = Teacher('Alice', 'Female', 'English')

print isinstance(t,Person)
print isinstance(t,Student)
print isinstance(t,Teacher)
print isinstance(t,object)

# 输出结果:
True
False
True
True

type(s):获取变量的类型,它返回一个 Type 对象。

type(123)

s = Student('Bob', 'Male', 88)
type(s)

# 输出结果
<type 'int'>
<class '__main__.Student'>

dir(s):获取变量的所有属性。

dir(123)   # 整数也有很多属性...

# 输出结果
['__abs__', '__add__', '__and__', '__class__', '__cmp__', ...]

getattr(s, attr_key) # 获取name属性
setattr(s, attr_key, attr_value) # 设置新的name属性

class Person(object):

    def __init__(self, name, gender, **kw):
        self.name = name
        self.gender = gender
        for key in kw:
            setattr(self,key,kw[key]) # 设置属性

p = Person('Bob', 'Male', age=18, course='Python')
print p.age
print p.course

# 输出结果:
18
Python

2. 多态

多态也是面向对象语言的特性之一。

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def whoAmI(self):
        return 'I am a Person, my name is %s' % self.name

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score
    def whoAmI(self):
        return 'I am a Student, my name is %s' % self.name

class Teacher(Person):
    def __init__(self, name, gender, course):
        super(Teacher, self).__init__(name, gender)
        self.course = course
    def whoAmI(self):
        return 'I am a Teacher, my name is %s' % self.name

def who_am_i(x):
    print x.whoAmI()

p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')

who_am_i(p)
who_am_i(s)
who_am_i(t)

# 输出结果:
I am a Person, my name is Tim
I am a Student, my name is Bob
I am a Teacher, my name is Alice

这种行为称为多态。也就是说,方法调用将作用在 x 的实际类型上。s 是Student类型,它实际上拥有自己的 whoAmI()方法以及从 Person继承的 whoAmI方法,但调用 s.whoAmI()总是先查找它自身的定义,如果没有定义,则顺着继承链向上查找,直到在某个父类中找到为止。

3. 多重继承

除了从一个父类继承外,Python允许从多个父类继承,称为多重继承。

class A(object):
    def __init__(self, a):
        print 'init A...'
        self.a = a

class B(A):
    def __init__(self, a):
        super(B, self).__init__(a)
        print 'init B...'

class C(A):
    def __init__(self, a):
        super(C, self).__init__(a)
        print 'init C...'

class D(B, C):
    def __init__(self, a):
        super(D, self).__init__(a)
        print 'init D...'

d = D('d')

# 输出结果:
init A...
init C...
init B...
init D...

D 同时继承自 B 和 C,也就是 D 拥有了 A、B、C 的全部功能。多重继承通过 super()调用init()方法时,A 虽然被继承了两次,但init()只调用一次。
多重继承的目的是从两种继承树中分别选择并继承出子类,以便组合功能使用。

五、特殊方法

特殊方法定义在class中,不需要直接调用,Python的某些函数或操作符会调用队员的特殊方法。

1. 正确使用特殊方法

  • 只需要编写用到的特殊方法
  • 有关联性的特殊方法都必须实现

2. 常见特殊方法

2.1 __str__和repr

如果要把一个类的实例变成str,就需要实现特殊方法__str__()。Python定义了__str__()和__repr__()两种方法,__str__()用于显示给用户,而repr()用于显示给开发人员。

class Person(object):

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

class Student(Person):

    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

    def __str__(self):
        return '(Student: %s, %s, %s)' %(self.name,self.gender,self.score)

    __repr__ = __str__

s = Student('Bob', 'male', 88)
print s

2.2 __cmp__

对 int、str 等内置数据类型排序时,Python的 sorted() 按照默认的比较函数 cmp 排序,但是,如果对一组 Student 类的实例排序时,就必须提供我们自己的特殊方法 __cmp__()。

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score
    def __str__(self):
        return '(%s: %s)' % (self.name, self.score)
    __repr__ = __str__

    def __cmp__(self, s):
        if self.name < s.name:
            return -1
        elif self.name > s.name:
            return 1
        else:
            return 0

上述 Student 类实现了cmp()方法,cmp用实例自身self和传入的实例s进行比较,如果 self 应该排在前面,就返回 -1,如果 s 应该排在前面,就返回1,如果两者相当,返回 0。

2.3 __len__

如果一个类表现得像一个list,要获取有多少个元素,就得用 len() 函数。要让 len() 函数工作正常,类必须提供一个特殊方法len(),它返回元素的个数。

class Students(object):
    def __init__(self, *args):
        self.names = args
    def __len__(self):
        return len(self.names)

只要正确实现了len()方法,就可以用len()函数返回Students实例的“长度”。

2.4 __slots__

由于Python是动态语言,任何实例在运行期都可以动态地添加属性。如果要限制添加的属性,例如,Student类只允许添加name、gender和score这3个属性,就可以利用Python的一个特殊的__slots__来实现。

class Student(object):
    __slots__ = ('name', 'gender', 'score')
    def __init__(self, name, gender, score):
        self.name = name
        self.gender = gender
        self.score = score

s = Student('Bob', 'male', 59)
s.name = 'Tim' # OK
s.score = 99 # OK
s.grade = 'A'
Traceback (most recent call last):
  ...
AttributeError: 'Student' object has no attribute 'grade'

__slots__的目的是限制当前类所能拥有的属性,如果不需要添加任意动态的属性,使用__slots__也能节省内存。

2.5 __call__

所有的函数都是可调用对象。一个类实例也可以变成一个可调用对象,只需要实现一个特殊方法__call__()。

# 把 Person 类变成一个可调用对象
class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

    def __call__(self, friend):
        print 'My name is %s...' % self.name
        print 'My friend is %s...' % friend

p = Person('Bob', 'male')
p('Tim')

# 输出结果:
My name is Bob...
My friend is Tim...

2.6 数学运算

四则运算不局限于int和float,还可以是有理数、矩阵等。要表示有理数,可以用一个Rational类来表示。

# Rational类,实现四则运算
class Rational(object):
    def __init__(self, p, q):
        self.p = p
        self.q = q

    def __add__(self, r):
        return Rational(self.p * r.q + self.q * r.p, self.q * r.q)

    def __sub__(self, r):
        return Rational(self.p * r.q - self.q * r.p, self.q * r.q)

    def __mul__(self, r):
        return Rational(self.p * r.p, self.q * r.q)

    def __div__(self, r):
        return Rational(self.p * r.q, self.q * r.p)

    def __str__(self):
        n = self.gcd(self.p,self.q)
        return '%s/%s' % (self.p/n, self.q/n)

    __repr__ = __str__

    def gcd(self,a,b):
        n,m = 1,1
        if a == b:
            return 1
        if a < b :
            if b%a == 0:
                return a
            n = a
        elif a > b:
            if a%b == 0:
                return b
            n = b
        for i in range(2,n/2+1):
            if a%i==0 and b%i==0:
                m = i

        return m


r1 = Rational(1, 2)
r2 = Rational(1, 4)
print r1 + r2
print r1 - r2
print r1 * r2
print r1 / r2

2.7 类型转换

Rational类实现了有理数运算,但是,如果要把结果转为 int 或 float 怎么办?

class Rational(object):
    def __init__(self, p, q):
        self.p = p
        self.q = q

    def __int__(self):
        return self.p // self.q

    def __float__(self):
        return self.p * 1.0 / self.q

print float(Rational(7, 2))
print float(Rational(1, 3))

2.8 @property

Python支持高阶函数,在函数式编程中我们介绍了装饰器函数,可以用装饰器函数把 get/set 方法“装饰”成属性调用。

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.__score = score
    @property
    def score(self):
        return self.__score
    @score.setter
    def score(self, score):
        if score < 0 or score > 100:
            raise ValueError('invalid score')
        self.__score = score

第一个score(self)是get方法,用@property装饰,第二个score(self,score)是set方法,用@score.setter装饰,@score.setter是前一个@property装饰后的副产品。

你可能感兴趣的:(Python学习)