Python学习笔记

Contents

  1. 1. Python基础
    1. 1.1. 数据类型和变量
    2. 1.2. 条件判断
    3. 1.3. 循环
    4. 1.4. 列表
    5. 1.5. 元祖tuple
    6. 1.6. 字典
    7. 1.7. 集合
  2. 2. 函数
    1. 2.1. 函数参数
    2. 2.2. 递归函数
    3. 2.3. 函数式编程
      1. 2.3.1. 高阶函数
      2. 2.3.2. 返回函数
      3. 2.3.3. 匿名函数:不需要显示地定义函数
      4. 2.3.4. 装饰器
      5. 2.3.5. 偏函数
  3. 3. 面向对象
    1. 3.1. 继承与多态
    2. 3.2. 对象信息
    3. 3.3. 实例属性和类属性
    4. 3.4. 多重继承
    5. 3.5. 定制类
    6. 3.6. 使用枚举类
    7. 3.7. 元类

由于项目需要,最近开始学Python。Python是一种面向对象、解释型语言。也是最近非常火的一种语言。Python为我们提供了非常完善的基础代码库,涵盖了网络、文件、GUI、数据库、文本等大量内容。Python非常简单,有一定面向对象知识的同学都可以很快地入门并深入。但是Python也有缺点:一个是运行速度慢;一个是代码不能加密。因为Python是解释型语言,所以代码在执行时会一行一行地翻译成CPU能理解地机器码。这个翻译过程很耗时。我学习Python的目的主要是为了编写爬虫代码和学习机器学习知识。所以对Python的学习程度并不需要达到深层次。我为了快速入门,选择廖雪峰的官方网站中的Python3教程进行学习。本文主要是我学习过程记下的笔记,着重于与java不太一样的地方。个人笔记仅供参考。

Python基础

数据类型和变量

  1. 整数:十六进制用0x前缀和0-9,a-f表示
  2. 浮点数:小数,科学计数法:10用e代替;整数和浮点数在计算机内部存储的方式是不同的,整数运算永远是精确的(包括除法),浮点数运算则可能会有四舍五入的误差
  3. 字符串和编码
    (1) 以单引号’或双引号”括起来的任意文本。
    (2) 可以用转义字符\来标识’和”。(\n:换行(Python允许使用’’’…’’’格式表示多行内容,eg。’’’a…b…c’’’),\t:制表符,\:表示\,)
    (3) r’’表示’’内部的字符默认不转义
    (4) 编码:

ASCII编码:1个字节(包括大小写英文字母,数字和一些符号)
Unicode编码:把所有语言都统一到一套编码里,清除乱码问题(多为2字节,把ASCII编码的字符用Unicode编码,在前面补8个0)
UTF-8:把Unicode编码转化为“可变长编码“,因为Unicode编码比ASCII编码需要多一倍的存储空间,若文本全为英文的话,及其不节约。
~~ASCII编码实际上可以被看成是UTF-8编码的一部分。
~~在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就装换为UTF-8编码
(5) Python3版本中,字符串是以Unicode编码,即支持多语言。ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符。
(6) 如果要在网络上传输,或者保存到磁盘上,就需要把字符串变为以字节为单位的bytes,用带b前缀的单引号或双引号表示:x=b’ABC’。encode()将字符变为bytes,decode()将bytes变为字符。

(7) 在操作字符串时,我们经常遇到str和bytes的互相转换。为了避免乱码问题,应当始终坚持使用UTF-8编码对str和bytes进行转换。
(8) 由于Python源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按UTF-8编码读取,我们通常在文件开头写上这两行:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
  • 第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释;
  • 第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。
  • 申明了UTF-8编码并不意味着你的.py文件就是UTF-8编码的,必须并且要确保文本编辑器正在使用UTF-8 without BOM编码:

(9) 格式化:%

%d:整数
%f:浮点数
%s:字符串
%x:十六进制整数
%%:%

  1. 布尔值:True和False;and、or和not运算
    在python中,and和or执行布尔逻辑运算时,并不返回布尔值,而是返回他们实际进行比较的值之一。在布尔上下文中从左到右演算表达式的值。对于and,如果布尔上下文中的所有值为真,那么and返回最后一个值;如果布尔上下文中的某个值为假,则and返回第一个假值。对于or,如果有值为真,则返回第一个真值,若所有值都为假,则返回最后一个假值。
  2. 空值:None,不能理解为0。
  3. 变量:变量名必须是大小写英文、数字和_的组合。
    动态语言:

    静态语言:


    b:ABC
  4. 常量:通常用全部大写的变量名表示常量
  5. 1.1+1.1+1.1=3.3000000000003:因为精度损失

条件判断

循环

  1. for x in ..
  2. while

列表

  1. list是一种有序的集合,可以随时添加和删除其中的元素
    classmates = [‘Michael’, ‘Bob’, ‘Tracy’]
  2. 用len()函数可以获得list元素的个数,用索引来访问list中每一个位置的元素,记得索引是从0开始的当索引超出了范围时,Python会报一个IndexError错误,所以,要确保索引不要越界,记得最后一个元素的索引是len(classmates) - 1。
  3. 如果要取最后一个元素,除了计算索引位置外,还可以用-1做索引,直接获取最后一个元素。
  4. list元素也可以是另一个list

元祖tuple

  1. 不可变的列表(指向永远不变);除改变列表内容的方法外,其他方法均适用于元组;因此:索引、切片、len、print可用;append、extend、del等不可用
    classmates = (‘Michael’, ‘Bob’, ‘Tracy’)
  2. 因为tuple不可变,所以代码更安全。如果可能,能用tuple代替list就尽量用tuple。
  3. 定义一个空的tuple:t = ()
  4. t=(1):定义的不是tuple,而是1这个数,只有1个元素的tuple定义时必须加一个逗号,。t=(1,)

字典

  1. Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度
    my_dict={‘john’:1234,’Mike’:5678,’Bob’:8765}
    my_dict[‘Bob’]
  2. 给定一个名字,比如’Michael’:95,dict在内部就可以直接计算出Michael对应的存放成绩的“页码”,也就是95这个数字存放的内存地址,直接取出来,所以速度非常快
  3. dict内部存放的顺序和key放入的顺序是没有关系的
  4. 和list比较,查找和插入的速度极快,不会随着key的增加而增加;需要占用大量的内存,内存浪费多。
  5. 要保证hash的正确性,作为key的对象就不能变。在Python中,字符串、整数等都是不可变的,因此,可以放心地作为key。而list是可变的,就不能作为key
  6. 字典预算符合方法

    len(my_dict)
    key in my_dict 快速判断key是否为字典中的键:O(1);==my_dict.has_key(key)
    for key in my_dict 枚举字典中的键:键是无序的
    my_dict.items() 全部的键-值对;返回的键值对是以列表的形式
    my_dict.keys() 全部的键
    my_dict.values() 全部的值
    my_dict.clear() 清空字典

集合

  1. 无序不重复元素(键)集;和字典类似,但是无“值”

    创建: x=set()

    x={key1,key2,...}
    

    添加和删除: x.add(‘body’)

    x.remove('body')
    

    集合的运算符: - & | !=

  2. set的原理和dict一样,所以,同样不可以放入可变对象,因为无法判断两个可变对象是否相等,也就无法保证set内部“不会有重复元素”
  3. 中文分词————算法:正向最大匹配(从左到右取尽可能长的词)
    (1) 加载词典:lexicon.txt

    def load_dic(filename):

    f= open(filename)
    word_dic=set()
    max_length=1
    for line in f:
        word=unicode(line.strip(),'utf-8')
        word_dict.add(word)
        if len(word)>max_len:
            max_len=len(word)
    return max_len,word_dict
    

(2) 正向最大匹配分词

def fmm_word_seg(sent,max_len,word_dict):
    begin=0
    words=[]
    sent=unicode(sent,'utf-8')
    while begin<len(sent):
        for end in range(begin+max_len,begin,-1):
            if sent[begin:end] in word_dict:
                words.append(sent[begin:end])
                break
        begin=end
    return words  

(3)应用

max_len,word_dict=load_dict('lexcion.txt')     
sent=raw_input('Input a sententce:')     
words =fmm_word_seg(sent,max_len,word_dict)
for word in words:
    print word

函数

函数参数

函数参数:位置参数、默认参数、可变参数、关键字参数(命名关键字参数)

  1. 位置参数:普通的传参方式
  2. 默认参数
    1
    2
    3
    4
    5
    6
    def power(x, n=2):
    s = 1
    while n > 0:
    n = n - 1
    s = s * x
    return s
1
2
3
>>> power(5)
25>>> power(5, 2)
25

!定义默认参数必须指向不可变对象

1
2
3
4
5
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L

  1. 可变参数:允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。
    1
    2
    3
    4
    5
    def calc(*numbers):
    sum = 0
    for n in numbers:
    sum = sum + n * n
    return sum
1
2
3
4
5
>>> calc(1, 2, 3)
14
>>> nums = [1, 2, 3]
>>> calc(*nums)
14
  1. 关键字参数:允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict
    作用:扩展函数的功能。用途:注册时,除必填项外,利用关键字参数还可以传入可选项。
    1
    2
    def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)
1
2
3
4
5
6
7
8
9
>>> person('Michael', 30)
name: Michael age: 30 other: {}
>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

extra表示把extra的一份拷贝传给kw,kw获得一个dict,对kw的改动不会影响到函数外的extra
命名关键字:限制关键字参数的名字,需要一个特殊分隔符后面的参数被视为命名关键字参数

1
2
3
4
def person(name, age, *, city, job):
print(name, age, city, job)
>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Enginee

  • 如果函数定义中已经有一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符
  • 命名关键字参数必须传入参数名,这和位置参数不同,如果没有传入参数名,视为位置参数,调用将报错
1
2
3
4
>>> person('Jack', 24, 'Beijing', 'Engineer')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: person() takes 2 positional arguments but 4 were given
  1. 参数组合
    在python中定义函数,可以组合使用上述5种参数,但参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def f1(a,b,c=0,*args,**kw):
    print('a=',a,'b=',b,'c=',c,'args=',args,'kw=',kw)
    def f2(a,b,c=0,*,d,**kw):
    print('a=',a,'b=',b,'c=',c,'d=',d,'kw=',kw)
    f1(1,2)
    f1(1,2,c=3)
    f1(1,2,3,'a','b')
    f1(1,2,3,'a','b',x=99)
    f2(1,2,d=99,ext=None)
    f2(1,2,dd=11,d=99,ext=None)
1
2
3
4
5
6
a= 1 b= 2 c= 0 args= () kw= {}
a= 1 b= 2 c= 3 args= () kw= {}
a= 1 b= 2 c= 3 args= ('a', 'b') kw= {}
a= 1 b= 2 c= 3 args= ('a', 'b') kw= {'x': 99}
a= 1 b= 2 c= 0 d= 99 kw= {'ext': None}
a= 1 b= 2 c= 0 d= 99 kw= {'ext': None, 'dd': 11}

最神奇的是通过一个tuple和dict,你也可以调用上述函数:

1
2
3
4
5
6
7
8
>>> args = (1, 2, 3, 4)
>>> kw = {'d': 99, 'x': '#'}
>>> f1(*args, **kw)
a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
>>> args = (1, 2, 3)
>>> kw = {'d': 88, 'x': '#'}
>>> f2(*args, **kw)
a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}

所以,对于任意函数,都可以通过类似func(args, *kw)的形式调用它,无论它的参数是如何定义的。

递归函数

函数调用是通过栈实现,所以使用递归函数需要注意防止栈溢出。
解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,可以把循环看成是一种特殊的尾递归函数。
尾递归:在函数返回的时候,调用自身本身,并且return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。

1
2
3
4
def fact(n):
if n==1:
return 1
return n * fact(n - 1)

1
2
3
4
5
6
7
def fact(n):
return fact_iter(n, 1)

def fact_iter(num, product):
if num == 1:
return product
return fact_iter(num - 1, num * product)

尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。
遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化,所以,即使把上面的fact(n)函数改成尾递归方式,也会导致栈溢出。

函数式编程

高阶函数

高阶函数: 把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。

  1. map/reduce
    (1) map()
    map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。 Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list。
    1
    2
    3
    4
    5
    def normalize(name):
    name=name.title()
    return name
    L=['adam','LISA','barT']
    print(list(map(normalize,L)))

(2)reduce()
reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

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

生成素数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def _odd_iter():
n=1
while True:
n=n+2
yield n
def _not_divisible(n):
return lambda x:x%n>0
def primes():
yield 2
it=_odd_iter()
while True:
n=next(it)
yield n
it = filter(_not_divisible(n),it)
for n in primes():
if n<1000:
print(n)
else:
break

yield:yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator, 调用primes() 不会执行 fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 primes() 函数内部的代码,执行到 yield n 时,primes函数就返回一个迭代值,下次迭代时,代码从 yield n 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。

  1. sorted
    sorted()也是一个高阶函数,用sorted排序的关键在于实现一个映射函数, 它可以接收一个key函数来实现自定义的排序
    1
    2
    3
    4
    >>> sorted([36, 5, -12, 9, -21])
    [-21, -12, 5, 9, 36]
    >>> sorted([36, 5, -12, 9, -21], key=abs)
    [5, 9, -12, -21, 36]

可以传入第三个参数reverse=True, 进行反向排序

1
2
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']

返回函数

  1. 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
  2. 内部函数可以引用外部函数的参数和局部变量,当外部函数返回内部函数时,相关参数和变量都保存在返回的函数中,这种程序结构称为闭包。
  3. 每次调用都会返回一个新的函数,即使传入相同的参数。
  4. 返回一个函数时,牢记该函数并未执行,直到调用了f()才执行。所以返回函数中不要引用任何可能会变化的变量。

匿名函数:不需要显示地定义函数

lambda x: x * x
  1. 关键字lambda表示匿名函数,冒号前面的x表示函数参数。
  2. 匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。
  3. 匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数

装饰器

  1. 函数是一个对象,可以赋值给一个变量,也可以通过变量来调用该函数。
  2. 若要增强某个函数的功能,比如在函数调用前后自动打印日志,但又不希望修改函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import functools
    def log(text):
    def decorator(func):
    @functools.wraps(func) #把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。
    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-1")

now() #相当于 now=log(‘execute’)(now)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import functools
def log(textOrFunc):
text = textOrFunc if isinstance(textOrFunc, str) else ''
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print(text)
print('bengin call %s()' % ( func.__name__))
func(*args, **kw)
print('end call %s()' % ( func.__name__))
return wrapper
return decorator if isinstance(textOrFunc, str) else decorator(textOrFunc)
@log('execute')
def now():
print("2015-3-1")
now()
print(now.__name__)

偏函数

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

1
2
3
4
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64


面向对象

继承与多态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Animal(object):
def run(self):
print("Animal is running")
class Dog(Animal):
def run(self):
print("Dog is running")
class Timer(object):
def run(self):
print("Timer is running")
def runtwise(animal):
animal.run()
animal.run()
runtwise(Animal())
runtwise(Dog())
runtwise(Timer())

因为python是动态语言(鸭子类型),所以不一定需要传入Animal类型或其子类型,只需要保证传入的对象有一个run()方法就可以了。

对象信息

  1. type():返回对象类型
    ~使用types模块中定义的常量来判断一个对象是否是函数。
  2. isinstance():判断对象是否是同一类型;对于class的继承关系,type()很不方便,可以使用isinstance()函数
  3. dir():获得一个对象的所有属性和方法,返回一个包含字符串的list

实例属性和类属性

因为python是动态语言,所以根据累创建的实例可以任意绑定属性

1
2
3
4
5
6
7
8
9
10
11
12
>>> 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

编写程序的时候,千万不要把实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。
Python允许在定义class的时候,定义一个特殊的slots变量,来限制该class实例能添加的属性:

1
2
3
4
5
6
7
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

>>> s = Student() # 创建新的实例>>> s.name = 'Michael' # 绑定属性'name'>>> s.age = 25 # 绑定属性'age'>>> s.score = 99 # 绑定属性'score'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'

  1. @property
    Python内置的@property装饰器就是负责把一个方法变成属性调用的: 既能检查参数,又可以用类似属性这样简单的方式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class Student(object):
    @property
    def score(self):
    return self._score
    @score.setter
    def score(self, value):
    if not isinstance(value, int):
    raise ValueError('score must be an integer!')
    if value < 0 or value > 100:
    raise ValueError('score must between 0 ~ 100!')
    self._score = value

    >>> s = Student()
    >>> s.score = 60 # OK,实际转化为s.set_score(60)>>> s.score # OK,实际转化为s.get_score()60>>> s.score = 9999
    Traceback (most recent call last):
    ...
    ValueError: score must between 0 ~ 100!

把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值, 还可以定义只读属性。

多重继承

1
2
class Dog(Mammal, Runnable):
pass

MixIn:一种常见的设计。MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。

定制类

1. str()与repr()
str()返回用户看到的字符串,而repr()返回程序开发者看到的字符串,也就是说,repr()是为调试服务的。

1
2
3
4
5
6
7
8
class Student(object):
def __init__(self, name):
self.name = name
def __str__(self):
return 'Student object (name=%s)' % self.name
__repr__ = __str__
>>> print(Student('Michael')
>>> Student('Michael')

2. iter
如果一个类想被用于for … in循环,类似list或tuple那样,就必须实现一个iter()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的next()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

1
2
3
4
5
6
7
8
9
10
11
12
class Fib(object):
def __init__(self):
self.a,self.b=0,1
def __iter__(self):
return self
def __next__(self):
self.a,self.b=self.b,self.a+self.b
if self.a>100:
raise StopIteration()
return self.a
for n in Fib():
print(n)

3. getitem
实现getitem()可以像list那样按照下标取出元素。
要正确实现一个getitem()还是有很多工作要做的,即切片和step参数
4. getattr :动态返回一个属性。
只有在没有找到属性的情况下,才调用getattr,已有的属性,不会在getattr中查找。
5. call :只需要定义一个call()方法,就可以直接对实例进行调用。

1
2
3
4
5
6
7
8
9
class Student(object):
def __init__(self, name):
self.name = name

def __call__(self):
print('My name is %s.' % self.name)
>>> s = Student('Michael')
>>> s() # self参数不要传入
My name is Michael.

通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。

1
2
3
4
>>> callable(Student())
True
>>> callable('str')
False

使用枚举类

为这样的枚举类型定义一个class类型: Enum类

1
2
3
4
5
6
7
8
9
10
11
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
from enum import Enum, unique
@uniqueclass Weekday(Enum):
Sun = 0 # Sun的value被设定为0
Mon = 1
Tue = 2
Wed = 3
Thu = 4
Fri = 5
Sat = 6

@unique装饰器可以帮助我们检查保证没有重复值。 既可以用成员名称引用枚举常量,又可以直接根据value的值获得枚举常量。

元类

class的定义是运行时动态创建的,而创建class的方法就是使用type()函数。
type()函数既可以返回一个对象的类型,又可以创建出新的类型,
要创建一个class对象,type()函数依次传入3个参数:

  1. class的名称;
  2. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
  3. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。

metaclass
除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。
先定义metaclass,就可以创建类,最后创建实例。