Stay hungry, Stay foolish

0%

python基础笔记

实用函数

开发调试之必备

  • id() 返回对象的唯一身份标识
  • type() 返回对象的类型
  • dir() 显示对象的属性(没有参数显示全局变量)

环境安装

PIP

全称是Python Package Index,Python的包管理器,可以理解为ubuntu下的apt-get

  • 安装
1
sudo apt-get install python-pip
  • 使用

安装python的mongodb扩展

1
pip install pymongo

virtualenv

一个创建隔离的Python环境的工具

  • 安装

使用apt-get

1
sudo apt-get install python-virtualenv

或者使用pip

1
sudo pip install virtualenv
  • 使用

建立虚拟环境

1
virtualenv MyENV

启动虚拟环境

1
2
cd MyEnv
source ./bin/activate

在虚拟环境中安装python包

1
pip install [包名称]

退出虚拟环境

1
deactivate

标准类型的分类

数据类型 存储模型 更新模型 访问模型
数字 标量 不可更改 直接访问
字符串 标量 不可更改 顺序访问
列表 容器 可更改 顺序访问
元组 容器 不可更改 顺序访问
字典 容器 可更改 映射访问

这里标准类型被分为三类,这里我们需要重点记一下更新模型(不管是在接下来的例子还是以后的编程中,还要打交道)

更新模型中的可更改不可更改分别代表着对象创建成功后,它的值可不可以进行更新。

数字和字符串之所以是不可更改,是因为事实上是创建一个新的对象取代一个旧的对象。

1
2
3
4
5
6
7
8
9
10
11
12
>>> x = 'abc'
>>> id(x)
140024151271304
>>> x = 'def'
>>> id(x)
140024115106120
>>> y = 1
>>> id(y)
34961336
>>> y += 1
>>> id(y)
34961312

再来看一下列表:

1
2
3
4
5
6
>>> aList = [1, 2, 3, 4]
>>> id(aList)
140024094989344
>>> aList[1] = 5
>>> id(aList)
140024094989344

序列

序列包括字符串、列表和元组。它们的成员是有序排列的,并且有着相同的访问模式——可以通过下标访问到它的一个(下标偏移量)或多个(切片)。

当然,它们有着通用的操作符:

  • 成员关系操作符(in, not in)
  • 连接操作符(+)
  • 重复操作符(*)
  • 切片操作符([], [:], [::])

还有相同的BIF(enumerate、len、max、min、reversed、sorted、sum、zip等)

当然,除了这些公共的BIF,他们还有着自己私有的BIF,可以通过dir()来查看,如:

1
2
>>> dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

具体的使用可以查看手册或API,这里就不一一列举了。

字符串不变性

对于字符串为什么会是不可更改,一个切片的例子可能比id()讲的更加的清晰明白:

1
2
3
4
5
6
7
8
>>> s = 'abc'
>>> s[2] = 'd'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> s = s[0]+'d'+s[2]
>>> s
'adc'

从本例中可以看到如果想修改字符串的值,就要用一点点hacker,否则就会报出TypeError的异常。

单元素元组的初始化

元组的分界符是圆括号,而圆括号却不仅仅属于元组(也用于分组)。所以单元素的初始化看起来有那么一点点hacker(需要第一个元素后添加一个逗号)。

1
2
3
4
>>> type(('xyz'))
<type 'str'>
>>> type(('xyz',))
<type 'tuple'>

元组的”可变性”

1
2
3
4
5
6
>>> t = (['xyz', 123], 23, -103,4)
>>> t
(['xyz', 123], 23, -103, 4)
>>> t[0][1] = 'abc'
>>> t
(['xyz', 'abc'], 23, -103, 4)

按前面的分类来讲,元组属于不可变类型,这里为何又可以改变了呢?——其实这里改变不是元组,而是列表。

深拷贝与浅拷贝

映射类型(字典)

创建一个“默认”字典

1
2
3
4
5
6
>>> ddict = {}.fromkeys(('x', 'y'), -1)
>>> ddict
{'y': -1, 'x': -1}
>>> edict = {}.fromkeys(('foo', 'bar'))
>>> edict
{'foo': None, 'bar': None}

键必须是可哈希的

这里又要说到更新模型了:所有不可变的类型都是可哈希的(元组中不能有可变类型对象——参见”元组的可变性”)

集合

类型

  • 可变集合(set)
  • 不可变集合(frozenset)

异常

try-except-else-finally

代码格式一览(大而全)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try:
try_suite
except Exception1:
suite_for_Exception1
except (Exception2, Exception3, Exception4):
suite_for_Exception2_3_and_4
except Exception5, Argument5:
suite_for_Exception5_plus_argument
except (Exception6, Exception7), Argument67:
suite_for_Exception6_and_7_plus_argument
except:
suite_for_all_other_exceptions
else:
no_exceptions_detected_suite
finally:
always_execute_suite

异常的继承结构

\- BaseException
    |- KeyboardInterrupt
    |- SystemExit
    |- Exception
        |- (all other current built-in exceptions)

上下文管理

其他

  • 触发异常(raise)
  • 断言(assert)
  • 异常与sys模块(sys.exc_info())

sys.exc_info()返回一个元组,包含以下三类信息:

  • exc_type:异常类
  • exc_value: 异常类的实例
  • exc_traceback: 跟踪记录对象
1
2
3
4
5
6
7
8
>>> try:
... float('abc')
... except:
... import sys
... exc_tuple = sys.exc_info()
...
>>> print exc_tuple
(<type 'exceptions.ValueError'>, ValueError('could not convert string to float: abc',), <traceback object at 0x7f59e9c08200>)

函数

参数组

1
func(*tuple_grp_nonkw_args, **dict_grp_kw_args)

其中tuple_grp_nonkw_args是以元组形式体现的非关键字参数组,dict_grp_kw_args是装有关键字参数的字典

Python所允许的函数调用的完整语法:

1
func(position_args, keyword_args, *tuple_grp_nonkw_args, **dict_grp_kw_args)

关键字参数

关键字参数的一个特性是:调用都可通过函数调用中的参数名字来区分参数,允许参数缺失及不按顺序

1
2
3
>>> def net_conn(host, port):
... pass
>>> net_conn(port='8080', host='localhost')

###可变长度的参数

非关键字可变长参数(元组)

1
2
3
4
5
6
7
8
9
10
11
>>> def tupleVarArgs(arg1, arg2='defaultB', *theRest):
... print 'formal arg1 : ', arg1
... print 'formal arg2 : ', arg2
... for eachXtrArg in theRest:
... print 'another arg:', eachXtrArg
...
>>> tupleVarArgs('abc', 123, 'xyz', 456.789)
formal arg1 : abc
formal arg2 : 123
another arg: xyz
another arg: 456.789

关键字变量参数(字典)

为了区分关键字参数和非关键字非正式参数,使用了双星号

1
2
3
4
5
6
7
8
9
10
11
12
>>> def dictVarArgs(arg1, arg2='defaultB', **theRest):
... print 'formal arg1:', arg1
... print 'formal arg2:', arg2
... for eachXtrArg in theRest.keys():
... print 'Xtra arg %s : %s' % (eachXtrArg, str(theRest[eachXtrArg]))
...
>>> dictVarArgs('one', d = 10, e = 'zoo', men = ('freud', 'gaudi'))
formal arg1: one
formal arg2: defaultB
Xtra arg men : ('freud', 'gaudi')
Xtra arg e : zoo
Xtra arg d : 10

调用带有可变长参数对象函数

  • 函数定义
1
2
3
4
5
6
7
8
9
>>> def newfoo(arg1, arg2, *nkw, **kw):
... print 'arg1 is : ', arg1
... print 'arg2 is : ', arg2
... for eachNKW in nkw:
... print ' non-keyword arg : ', eachNKW
... for eachKW in kw.keys():
... print ' keyword arg %s : %s' %(eachKW, kw[eachKW])
...
>>>
  • 可变长参数对象
1
2
3
4
5
6
7
>>> newfoo(2, 4, *(6,8), **{'foo':10, 'bar':12})
arg1 is : 2
arg2 is : 4
non-keyword arg : 6
non-keyword arg : 8
keyword arg foo : 10
keyword arg bar : 12
1
2
3
4
5
6
7
8
9
10
11
12
>>> aTuple = (6,7,8)
>>> aDict = {'z':9}
>>> newfoo(1, 2, 3, x=4, y=5, *aTuple, **aDict)
arg1 is : 1
arg2 is : 2
non-keyword arg : 3
non-keyword arg : 6
non-keyword arg : 7
non-keyword arg : 8
keyword arg y : 5
keyword arg x : 4
keyword arg z : 9

列表解析与生成器表达式

  • 列表解析
1
sum([x ** 2 for x in range(6)])
  • 生成器表达式
    • 是列表解析的扩展
    • 延迟计算
    • 生成器允许你返回一个值,然后“暂停”代码的执行,稍后恢复
1
sum( x ** 2 for x in range(6))

面向对象编程

类属性VS实例属性

定义类和属性

1
2
3
4
>>> class C(object):
... version = 1.2 #不可变
... x = {2003 : 'poe2'} #可变
...

改变不可变对象

  • 实例化
1
2
3
4
5
>>> c = C()
>>> C.version
1.2
>>> c.version
1.2
  • 通过类改变version的值
1
2
3
4
5
>>> C.version += 0.1
>>> c.version
1.3
>>> C.version
1.3
  • 通过实例改变version的值
1
2
3
4
5
>>> c.version += 0.1
>>> c.version
1.4
>>> C.version
1.3

改变可变对象

  • 实例化
1
2
3
>>> foo = C()
>>> foo.x
{2003: 'poe2'}
  • 通过实例改变x
1
2
3
4
5
>>> foo.x[2004] = 'valid path'
>>> foo.x
{2003: 'poe2', 2004: 'valid path'}
>>> C.x
{2003: 'poe2', 2004: 'valid path'}

绑定方法VS非绑定方法

  • 调用一个方法的途径
    • 定义类和方法
    • 创建一个实例
    • 用这个实例调用方法

self参数代表实例对象本身,当用实例调方法时,由解释器传递给方法

  • 调用绑定方法
1
2
mc = MyClass()
mc.foo()
  • 调用非绑定方法
  • 派生子类、覆盖父类方法
1
2
3
class EmpAddrBookEntry(AddrBookEntry):
def __init__(self, nm, ph, em):
AddrBookEntry.__init__(self, nm, ph)
  • 多重继承

多重继承采用MRO算法来决定调用的方法,如下例中调用gc.foo()执行的是P1的foo方法,这时如果我想执行P2的foo方法怎么办呢?这时就需要用典型的非绑定方式去调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class P1(object):
def foo(self):
print 'called P1-foo()'

class P2(object):
def foo(self):
print 'called P2-foo()'

class C1(P1, P2):
pass

class C2(P1, P2):
def bar(self):
print 'called C2-bar()'

class GC(C1, C2):
pass

gc = GC()
gc.foo() # GC=>C1=>C2=>P1
gc.bar() # GC=>C1=>C2
P2.foo(gc)

静态方法和类方法

装饰器

  • 无参数装饰器
1
2
@deco
def foo(): pass
  • 有参数装饰器
1
2
3
@decomark(deco_args)
def foo(): pass
#等价于 foo = decomark(deco_args) (foo)

装饰器可以如函数调用一样“堆叠”起来

1
2
3
4
5
6
@g
@f
def foo():
pass

#等价于foo = g(f(foo))

staticmethod()和classmethod()内建函数

  • 早期是这样实现的
1
2
3
4
5
>>> class TestClassMethod:
... def foo(cls):
... print 'test class method'
...
... foo = classmethod(foo)
  • 有了装饰器之后
1
2
3
4
5
6
7
8
9
class TestStaticMethod:
@staticmethod
def foo():
pass

class TestClassMethod:
@classmethod
def foo(cls):
pass

通常的方法需要一个实例(self)作为第一个参数。而对于类方法而言,需要类而不是实例作为第一个参数,它是由解释器传给方法。类不需要特别的命名,类似self,不过很多人使用cls作为变量名称

私有化

  • 双下划线

双下划线实现私有性的原理是把属性加上类名形成新的“混淆”结果以防止和祖先类或子孙类中的同名冲突

1
2
3
4
5
6
7
8
9
10
>>> class NumStr(object):
... __num = 10
...
>>> NumStr.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'NumStr' objects>, '__weakref__': <attribute '__weakref__' of 'NumStr' objects>, '__module__': '__main__', '_NumStr__num': 10, '__doc__': None})
>>> NumStr()._NumStr__num
10
>>> c = NumStr()
>>> c._NumStr__num
10

__dict__ 与 __slots__ 类属性

字典位于实例的“心脏”。dict属性跟踪所有实例属性。
字典会占据大量内存,如果有一个属性少的类,但有很多实例。为了内存上的考虑,可使用slots代替dict

1
2
3
4
5
6
7
8
9
>>> class SlottedClass(object):
... __slots__ = {'foo', 'bar'}
...
>>> c = SlottedClass()
>>> c.foo = 42
>>> c.xxx = '133'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'SlottedClass' object has no attribute 'xxx'

类的实例

Python给类提供了__call__的特别方法,该方法允许程序员创建可调用的对象实例。(默认__call__()没有实现 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>>> class C(object):
... pass
...
>>> c = C()
>>> c
<__main__.C object at 0x7f0642abfb10>
>>> callable(c)
False
>>> c()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'C' object is not callable
>>>
>>> class D(object):
... def __call__(self, *args):
... print "I'm callable! args:\n", args
...
>>> d = D()
>>> d
<__main__.D object at 0x7f0642abfbd0>
>>> callable(d)
True
>>> d()
I'm callable! args:
()
>>> d(1, 2, 3)
I'm callable! args:
(1, 2, 3)
据说打赏我的人,代码没有BUG