Noob Notes - papatwo/PythonNoob GitHub Wiki
- python是一种解释型语言(需要逐行翻译给CPU);C是一种编译型语言(CPU直接执行)
- 解释型语言运行速度慢,但无法加密(必须发布源代码);编译型语言运行速度快,可加密(编译后有机器码,eg. exe文件)
- 官方解释器CPython,命令行运行
python
启动 - 交互解释器IPython(即Jupyter Notebook)在浏览器上进行交互
-
CPython
startpython
exitexit()
-
IPython
Anaconda -
print message
print 'message'
- save script as
.py
format - cd to directory in terminal and
python filename.py
-
直接运行
在.py
文件第一行加上#!/usr/bin/env python
, 然后在终端输入命令$ chmod a+x hello.py
,即可在终端输入./path/filename.py
直接运行.py
文件
- 输入:
var = raw_input('prompt msg')
- 输出:
print var
Python支持多种数据类型,在计算机内部,可以把任何数据都看成一个“对象”,而变量就是在程序中用来指向这些数据对象的,对变量赋值就是把数据和变量给关联起来 ——廖雪峰python教程
>>>'Hello, %s' % 'world' # 用%s占位,后接%内容
'Hello, world'
>>> 'Hi, %s, you have $%d.' % ('Mary', 100)
'Hi, Mary, you have $100.'
有几个%?占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个%?,括号可以省略。
常见的占位符有:
%d 整数
%f 浮点数
%s 字符串
%x 十六进制整数
list可以增加删除元素,用[]
框住;tuple元素确定后不可更改,用()
框住
index都从0开始
- 条件判断
if 条件 : #注意冒号
do something
if 条件 :
do something
else :
do some other things
if 条件:
do something
elif 条件2 : #可以有多个elif
do something
else :
do some other things
- 循环
-
for ... in
依次迭代list里的元素
names = ['a', 'b', 'c']
for name in names :
print name
所以
for x in ...
循环就是把每个元素代入变量x,然后执行缩进块的语句。
while
while 条件 :
do something
- dict
names = ['a', 'b', 'c']
scores = [1, 2, 3]
#等同于
d = {'a':1, 'b':2, 'c':3} #查找速度更快,注意curly bracket
>>> d = ['a']
1
dict的key必须是不可变对象因为通过key计算位置的算法Hash算法要保证其正确性,eg. list不能作为key
- set
set中储存的key没有重复的,相当于matlab中的
unique
且会做sort(如果是数字的话) 用add(key)
和remove(key)
分别可做key的增加和减少,但重复添加key没有效果
str是不变对象
'abc'
; list是可变对象['a', 'b', 'c']
>>> a = 'abc' # 'abc'为字符串,不可变对象;a为变量,指向内容'abc'
'abc'
>>> a.replace('a','A') # 变量a指向的内容
'Abc'
>>> a
'abc' # 然而a本身指向的内容还是'abc'
需要储存更改后的字符串需要将其assign到新的变量中,因为str本身是不可变的。
对于不变对象来说,调用对象自身的任意方法,也不会改变该对象自身的内容。相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可变的。
- 可以把库里面已有的函数名assign到一个变量里,然后用这个变量名做为函数来call
>>> a = abs
>>> a(-1)
1
- 自定义函数时有时需要定义默认参数,在定义参数时用等号定义:
def enroll(name, gender, age=6, city='Beijing'):
print 'name:', name
print 'gender:', gender
print 'age:', age
print 'city:', city
定义默认参数要牢记一点:默认参数必须指向不变对象!
- 参数类型:
可变参数既可以直接传入:func(1, 2, 3),又可以先组装list或tuple,再通过args传入:func((1, 2, 3));
关键字参数既可以直接传入:func(a=1, b=2),又可以先组装dict,再通过kw传入:func({'a': 1, 'b': 2})。
- 递归:
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式,例如
n * fact(n-1)
。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。
- python的index从0开始
- 几个例子:
>>> L[0:3] # 取0-2(不包括3)
>>> L[:3] # 取3之前的所有
>>> L[-2:] # 从倒数第二个去到最后 -2:end
>>> L[-2:-1] # 从倒数第二个取到最后的前一个(不包括最后一个) -2:end-1
>>> L[:] # 复制整个list
>>> (0, 1, 2, 3, 4, 5)[:3] # 切片一个tuple
(0, 1, 2)
>>> 'ABCDEFG'[::2] # 切片一个string,从头到尾割2个。'ABCDEFG'[:6:2]:第一个到第六个,每2个切一次 即matlab里的1:2:6
'ACEG'
>>> [x * x for x in range(1, 11) if x % 2 == 0] # 可以直接在列表里写生成条件
[4, 16, 36, 64, 100]
>>> [m + n for m in 'ABC' for n in 'XYZ'] # 还可以套N个loop
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
# 创造生成器以储存算法,节约储存空间
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10)) # 讲列表的[]改成()
>>> g # 获得一个generator,用于储存生成列表的算法
<generator object <genexpr> at 0x104feab40>
# 如果要一个一个打印出来,可以通过generator的next()方法,但是过于麻烦
>>> g.next()
0
>>> g.next()
1
>>> g.next()
4
# 一般使用for循环迭代,generator也是可迭代对象
>>> g = (x * x for x in range(10))
>>> for n in g:
... print n
...
0
1
4
9
16
25
36
49
64
81
# 在函数中,用yield代替等号,则该函数被视为一个generator,以Fibonacci数列生成为例
# 函数表达
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print b
a, b = b, a + b
n = n + 1
# generator表达
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b # 变化!!!
a, b = b, a + b
n = n + 1
这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
一个函数的返回值可以为数字,也可以为一个函数,eg.来自廖雪峰老师教程:
def lazy_sum(*args): #局部变量args
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
#当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:
>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function sum at 0x10452f668>
#调用函数f时,才真正计算求和的结果:
>>> f()
25
返回函数不要引用任何循环变量,或者后续会发生变化的变量。
lambda x: x * x
等同于def f(x): return x * x
,传入函数时如果是简单函数可用匿名函数比较方便,但它只能写一个表达式,不用谢return,返回值为表达式结果。
-
表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释
-
sys
模块有一个argv
变量,用list存储了命令行的所有参数。argv至少有一个元素,因为第一个参数永远是该.py文件的名称,例如:
运行python hello.py获得的sys.argv就是['hello.py'];
运行python hello.py Michael获得的sys.argv就是['hello.py', 'Michael']。 - 作用域
类似
__xxx__
这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author__
,__name__
就是特殊变量,我们自己的变量一般不要用这种变量名; 类似_xxx
和__xxx
这样的函数或变量就是非公开的(private),不应该被引用
- 调用module里的函数时先在python里
import moduleName
,然后用moduleName.functionName(var)
- 以图片模块为例:
安装命令:pip install ModuleName,此处图片模块名字为PIL(python image library)。但是PIL只支持32位系统,使用第三方的pillow(PIL的fork),即pip install pillow
。 安装完成后进入python,导入PIL并调用PIL里的函数:
>>> python
>>> from PIL import Image # 因为安装的是pillow,不能直接import Image
>>> image=Image.open("/path/fileName") # 图片文件
>>> image.show() # 显示图片
>>> print image.format, image.size, image.mode # 获取图片信息
MPO (6016, 4016) RGB
>>> image.thumbnail((200, 100)) # 生成一个略缩图
>>> image.save('Full path+file name+format', 'JPEG') # 储存
默认情况下,Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中:
>>> import sys
>>> sys.path
['', '/Library/Python/2.7/site-packages/pycrypto-2.6.1-py2.7-macosx-10.9-intel.egg', '/Library/Python/2.7/site-packages/PIL-1.1.7-py2.7-macosx-10.9-intel.egg', ...]
如果我们要添加自己的搜索目录,有两种方法:
- 一是直接修改sys.path,添加要搜索的目录:
>>> import sys
>>> sys.path.append('/Users/michael/my_py_scripts')
这种方法是在运行时修改,运行结束后失效。
第二种方法是设置环境变量PYTHONPATH,该环境变量的内容会被自动添加到模块搜索路径中。设置方式与设置Path环境变量类似。注意只需要添加你自己的搜索路径,Python自己本身的搜索路径不受影响。
#!/usr/bin/env python
# -*- coding: utf-8 -*- #标准注释
' a test module ' #一个字符串,表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释
__author__ = 'Michael Liao'
import sys #导入该模块
#sys模块有一个argv变量,用list存储了命令行的所有参数。argv至少有一个元素,因为第一个参数永远是该.py文件的名称
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()
测试一下自定义的hello模块
$ python hello.py
Hello, world!
$ python hello.py Michael
Hello, Michael!
或者启动交互环境python
$ python
Python 2.7.5 (default, Aug 25 2013, 00:04:04)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello
>>>
然后执行test()函数
>>> hello.test()
Hello, world!
导入模块时使用别名可根据当前运行环境选择最合适的模块 例如String.IO和cStringIO的选择
try:
import cStringIO as StringIO
except ImportError: # 导入失败会捕获到ImportError
import StringIO
这样就优先导入cString,如果cString不支持再导入String
private variable:_var_name
or __var_name
类 (抽象的)模板
实例 根据模板创建的具体对象
定义类 class
,紧接类名,紧接(object)--表该类是从哪个类继承下来的:
class Student(object):
pass
创建实例, 类名+()
>>> bart = Student()
>>> bart
<_main_.Student object> # 指向Student里的一个object
>>> Student
<class>
# 给实例bart绑定属性,直接define:
>>> bart.name = 'Bart Simpson'
>>> bart.name
'Bart Simpson'
或者是通过initialise类的模板来绑定:
class Student(object)
def _init_(self, name, score): # 第一个参数一定是self,指向实例本身,后面的属性绑定在self上,但创建的时候不用传self
self.name = name
self.score = score
通过init创建实例:
>>> bart = Student('Bart Simpson', 59)
>>> bart.name
'Bart Simpson'
>>> bart.score
59
对实例中的数据进行访问,可直接在类中定义一个访问函数,从而封存数据,不再从外界调用函数访问:
class Student(object)
def _init_(self, name, score): # 第一个参数一定是self,指向实例本身,后面的属性绑定在self上,但创建的时候不用传self
self.name = name
self.score = score
def print_score(self):
print '%s: %s' % (self.name, self.score)
直接从封装数据中访问数据:
>>> bart.print_score()
Bart Simpson: 59
用封装给Student类增加新的方法:
class Student(object)
def _init_(self, name, score): # 第一个参数一定是self,指向实例本身,后面的属性绑定在self上,但创建的时候不用传self
self.name = name
self.score = score
def print_score(self):
print '%s: %s' % (self.name, self.score)
def get_grade(self):
if self.score >=90:
return ‘A’
elif self.score >=60:
return 'B'
else:
return 'C'
然后通过实例直接调用get_grade函数:
>>> bart.get_grade()
'C'
定义一个class(类)的时候,可以继承其他类的attribute:
# 父类
class Animal(object):
def run(self):
print 'Animal is runing...'
子类Dog和Cat时就可以从Animal类继承他的attribute,但其实他们俩什么事都没干:
class Dog(Animal):
pass
class Cat(Animal):
pass
运行:
>>> Dog.run()
'Animal is running...'
>>> Cat.run()
'Animal is running...'
在子类里加入子类自己的新attribute:
class Dog(Animal):
def run(self):
print 'Dog is running...'
def eat(self):
print 'Eating meat...'
这时,子类Dog就存在了一个和父类Animal一样的attribute,运行时则会调用子类的attribute,获得多态: N.B 类也属于一种数据类型,判断一个变量是什么类型用:
>>> a = list() # a是list类型
>>> b = Animal() # b是Animal类型
>>> c = Dog() # c是Dog类型
>>> isinstance(a, list)
True
>>> isinstance(b, Animal)
True
>>> isinstance(c, Dog)
True
>>> isinstance(c, Animal)
True # c不仅是Dog还是Animal,因为Dog是从Animal继承下来的一个类,子类可是是父类的数据类型,但父类不能使子类的数据类型
如何理解多态的好处: 写一个函数,输入为一个Animal类型的变量:
def run_twice(animal): # animal是一个animal类型的数据,不是Animal这个类本身
animal.run()
animal.run()
运行:
>>> run_twice(Animal())
'Animal is running...'
'Animal is running...'
>>> run_twice(Dog())
'Dog is running...'
'Dog is running...'
>>> run_twice(Cat())
'Cat is running...'
'Cat is running...'
这个时候再加一个类型,从Animal class派生:
class Tortoise(Animal):
def run(self):
pring 'Tortoise is running slowly...'
调用run_twice()
时传入Tortoise得到:
>>> run_twice(Tortoise())
'Tortoise is running slowly...'
'Tortoise is running slowly...'
多态的好处就是,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态的意思:对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则: 对扩展开放:允许新增Animal子类; 对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。
判断数据类型可使用以下几种函数:
type(something) # 方便对independent的数据进行判断
isinstance(instance, class) # 方便对instance-class之间的关系进行判断
>>> a = Animal()
>>> d = Dog()
>>> h = Husky()
>>> isinstance(h, Husky)
True
>>> isinstance(h, Animal)
True
使用dir()
获得一个对象所有的属性和方法
使用len()
或something.__len__()
获取长度,后者是前者内部的方法,所以如果自己写的class也想用len()就写一个__len
__(),如下:
>>> class myOjbt(object):
... def __len__(self):
... return 100
...
>>>obj = myOjbt()
>>>len(obj)
100
使用lower()
返回小写字符串
使用hasattr()
判断对象里是否有某属性
使用setattr()
直接在对象里设置新/更改属性
使用getattr()
获取属性值