Python笔记
Table of Contents
语言要点
特性简介
Python对程序格式有较强的限定,如下所示:
- 注释符为
#
- 无需分号表示语句结束,长语句必要时需加
\
续行 - 用相同缩进来表达同一语句块,建议用4个空格作为缩进
- 默认只接受ASCII字符集,故通常需编码声明:
# -*- coding: utf-8 -*-
数据与操作
数据类型
- 数字类型:整型、浮点型和复数型
- 序列类型:字符串、元组、列表
- 映射类型:集合、字典
- 空类型:None对象
- 可调用类型:实现了
__call__()
方法的类 - 布尔类型:语义比较广泛,通常非空为True,空集为False
基本操作
del
解除变量或序列.
取对象中成员[]
取集合中元素()
函数调用- 比较操作允许链式表达
a < b < c < d
- 没有三目算符,但有三元表达:
returntrue if condition else returnfalse
- 所有序列都支持元素存在测试:
if item in seqs
列表操作
x = [1, 2, 3, 4, 5] del x[1]; print x del x[::2]; print x
[1, 3, 4, 5] [3, 5]
- l.count(x)
- x出现次数
- l.index(x)
- x出现下标
- l.append(x)
- 在尾部加入x
- l.extend(x)
- 插入x中的所有元素,append则是将x当作一个元素插入
- l.insert(i, x)
- 在下标i处插入x
- l.remove(x)
- 删除x第一次出现,如果未出现则引发异常
- l.pop(i=-1)
- 删除下标为i的元素,返回删除的元素,如果下标越界,引发异常
- l.reverse()
- 反转链表,无返回值
- l.sort(cmp=None, key=None, reverse=False)
- 如指定key则以key(x)替代x参与运算
集合操作
- s.copy()
- 影子复制
- s.difference(s1)
- s出现s1未出现
- s.intersection(s1)
- s和s1同时出现
- s.issubset(s1)
- s是s1的子集
- s.issuperset(s1)
- s1是s的子集
- s.symmetric_difference(s1)
- 只在s和s1其一出现
- s.union(s1)
- 在s或s1中出现
- s.add(x)
- 添加x到集合
- s.clear()
- 清除集合
- s.discard(x)
- 删除x,如果不存在也没关系
- s.pop()
- 随机弹出一个元素并返回
- s.remove(x)
- 如果x不存在会产生异常
字典操作
- d.copy()
- 影子复制
- d.has_key(x)
- 测试是否有键x
- d.items()/d.iteritems()
- 返回(key, value)对列表/迭代器
- d.keys()/d.iterkeys()
- 键列表/迭代器
- d.values()/d.itervalues()
- 值列表/迭代器
- d.get(k, x=None)
- 这个函数非常有用,如果k存在就返回d[k],否则返回x
- d.clear()
- 清除字典
- d.update(d1)
- 等价于:
for k in d1: d[k] = d1[k]
- d.setdefault(k, x=None)
- 如果k存在就返回d[k],否则将d[k]设置为x并返回
- d.pop(k, x=None)
- 弹出d[k],没有找到就返回x,如果未指定x又没找到d[k]会引发异常
- d.popitem()
- 随机弹出一个元素
流程控制
分支语句
if condiiton: statements elif condition: statements else: statements
要判断一个变量是否为真,直接的方式是if x
,请不要使用如下的一些形式:
if x is True if x == True if bool(x)
循环语句
while condition:
statements
for item in iterable: statements
while condition: statements else: statements # 只要while没有被break就会执行
[expression for item in iterable clauses] [x + 1 for x in range(10)] [x + 1 for x in range(10) if x % 2] [x + y for x in range(10) for y in range(10)]
跳出循环有两种方式:
- break
- 退出循环
- continue
- 进入下一轮循环
函数
def functionname(parameters): # 命名函数 statements lambda parameters: expression # 匿名函数
函数默认将变量绑定到局部名称空间,要使用全局的名字,就需要在函数中作如下声明。
global indentifiers
属性
def sum_args(*nums): '''accept arbitrary numerical arguments and return their sum''' return sum(nums) print sum_args.__name__ print sum_args.__doc__ print sum_args(1, 2, 3)
sum_args accept arbitrary numerical arguments and return their sum 6
生成器
yield expression
def updown(n): for x in range(n): yield x for x in range(n - 1, -1, -1): yield x for i in updown(10): print i,
0 1 2 3 4 5 6 7 8 9 9 8 7 6 5 4 3 2 1 0
闭包与装饰器
函数装饰器是通过嵌套函数来实现的,或者说是闭包来实现的。闭包的主要特点是即使外部函数已经返回,内部函数依然可以访问外部函数的局部变量。
def func_log(func): n = [0] class last: pass last.arg = None last.val = None def log(arg): n[0] += 1 line = 'call {}th {}({}) = {}, last call {}({}) = {}' print line.format(n[0], func.__name__, arg, func(arg), func.__name__, last.arg, last.val) last.arg = arg last.val = func(arg) return log @func_log def power(n): return n ** 2 for i in range(3): power(i)
call 1th power(0) = 0, last call power(None) = None call 2th power(1) = 1, last call power(0) = 0 call 3th power(2) = 4, last call power(1) = 1
Python2不允许修改外部作用域的局部变量,上面的例子分别通过修改可变类型和定义局部类型来实现修改外部作用域的值。
闭包本身可以看作是函数加上引用环境,当内部函数引用外部变量时,编译器/解释器会把函数和涉及到的引用环境打包为一个整体,所以称作闭包。
注意事项:函数只有在执行时才会去查找变量的值:
flist = [] for i in range(3): def foo(x): print x + i flist.append(foo) for f in flist: f(2)
4 4 4
比包的用途不仅在于提供装饰器,也可用于保存运行环境、根据外部变量来生成不同结果等。
类与继承
基本概念
Python2支持经典模型和新风格,Python3只支持新风格,所以最好是只使用单根继承。类型存储静态字段和方法,实例只存储数据成员,也称为attribute
。访问对象成员时,按照如下顺序查找:
instance.__dict__
、class.__dict__
、baseclass.__dict__
。
注意不要和名字查找混淆,名字查找采用LEGB
顺序在不同的作用域查找:
- locals
- 函数内部名字空间,形参和局部变量
- enclosing
- 嵌套函数的外部函数
- globals
- 函数所在模块名字空间
- __builtins__
- 内置模块名称空间
私有字段建议单下划线开头,提示即可。
属性property
由getter()
、setter()
、deleter()
几个方法构成。从字面上attribute
和property
都是属性,但前者是比较直接的数据成员,后者在行为上和数据成员相同,但是在实现上来看是将成员函数伪装成数据成员。
class Example(object): @property def name(self): return self.__name @name.setter def name(self, val): self.__name = val @name.deleter def name(self): del self.__name e = Example() e.name = "example" print e.__dict__, e.name del e.name print e.__dict__
{'_Example__name': 'example'} example {}
从实现上看,属性property
是特殊处理过的attribute
,所以访问优先级高于同名数据成员。
特殊方法
要实现类的方法,需要添加装饰器修饰:
class Example(obejct): @staticmethod def static_print(os): print os
特殊方法:
- __new__()
- 创建对象实例
- __init__()
- 初始化对象状态
- __del__()
- 对象回收前调用
修改字段要用指定函数:
- setattr(obj, "name", value)
u.name = value
- hasattr(obj, "name")
obj.__dict__["name"]
- getattr(obj, "name", default=None)
- 如果未找到返回
default
值 - delattr(obj, "name")
del obj.__dict__["name"]
索引操作符[]
也可以重载,自定义__setitem__
、__getitem__
和__delitem__
即可。函数对象/仿函数通过定义__call__()
来重载。
- __getattr__()
- 访问不存在的成员
- __setattr__()
- 对成员赋值
- __delattr__()
- 删除成员
- __getattribute__()
- 访问任何存在或不存在的成员,包括
__dict__
在定义这些函数的时候,不能调用setattr
、getattr
等函数,因为会造成死循环,只能直接访问__dict__
。而__getattribute__
更狠,会拦截__dict__
,所以它唯一能调用的就是基类的__getattribute__
。
继承
Python提供了两个函数来判定类型关系:
- issubclass(a, base)
- 判断a是否是base的继承
- isinstance(a, class)
- 判断a是否是class的实例
多重继承MRO(method resolution order):从下到上,从左到右。
class A(object): def print_a(self): print "a:a" class B(object): def print_a(self): print "b:a" def print_b(self): print "b:b" class C(A, B): def print_c(self): base = super(C, self) base.print_a() base.print_b() C().print_c()
a:a b:b
Python也支持抽象类,只需要添加修饰即可,派生类必须实现所有抽象函数/属性才能实例化:
from abc import ABCMeta, abstractmethod, abstractproperty class AbsExample(object): __metaclass__ = ABCMeta @abstractmethod def print_id(self): pass name = abstractproperty() class Example(AbsExample): def print_id(self): print "id" name = property(lambda s: s._name, lambda s, v: setattr(s, "_name", v)) e = Example()
Python还有一个特性叫开放类,可以在运行期增加/删除对象成员。
def print_id(self): print self.id def print_class_id(cls): print "class: ", cls def print_static(): print "static" class User(object): def __init__(self): self.id = "self id" User.print_id = print_id User.print_class_id = classmethod(print_class_id) User.print_static = staticmethod(print_static) u = User() u.print_id() User.print_class_id() User.print_static()
self id class: <class '__main__.User'> static
注意事项
兼容性问题
输入和输出
在Python2中print是一个表达式,而Python3中print是一个函数,因此在写print时,建议用函数式写法。
在Python2中input函数会计算用户的输入,要防止计算就需要用raw_input, Python3中将raw_input删除了,而input的含义等同于python2中的raw_input。
字典
在python2中字典有两个迭代函数,一个返回列表,一个返回迭代器。
- d.items()
- 返回(key, value)对列表
- d.iteritems()
- 返回(key, value)对迭代器
但在Python3中,直接将迭代器形式删除了,而原来的列表形式变成了迭代器形式。所以为了兼容性,还是推荐Python3的写法。
- d.items()
- 返回(key, value)对迭代器
注意事项
慎用__del__()方法
文章 慎用python的__del__方法 比较详细分析了这个缺陷。从语义上看,__del__
相当于C语言中的析构函数,但是问题是它不一定被调用。也就是说如果你实现了__del__
方法就不允许出现循环引用,否则垃圾回收器将因为不知道释放顺序而放弃释放。
class A(object): def __init__(self, parent): print "A init" self.parent = parent def __del__(self): print "A del" class B(object): def __init__(self): print "B init" self.child = A(self) def __del__(self): print "B del" b = B()
B init A init
内存泄漏
Python中的内存泄漏主要有两方面的原因:一种情况是对于长期运行程序而言,实例没有及时释放所有的引用导致内存泄漏。另一种情况是出现交叉引用,垃圾回收器不知道释放顺序而放弃释放。