Noob Notes - papatwo/PythonNoob GitHub Wiki

Welcome to the PythonNoob wiki!

记点小笔记


Intro

  1. python是一种解释型语言(需要逐行翻译给CPU);C是一种编译型语言(CPU直接执行)
  2. 解释型语言运行速度慢,但无法加密(必须发布源代码);编译型语言运行速度快,可加密(编译后有机器码,eg. exe文件)

解释器

  1. 官方解释器CPython,命令行运行python启动
  2. 交互解释器IPython(即Jupyter Notebook)在浏览器上进行交互

运行交互环境

  1. CPython
    start python exit exit()

  2. IPython
    Anaconda

  3. print message
    print 'message'

运行程序

  1. save script as .py format
  2. cd to directory in terminal and python filename.py
  3. 直接运行
    .py文件第一行加上 #!/usr/bin/env python, 然后在终端输入命令 $ chmod a+x hello.py,即可在终端输入./path/filename.py直接运行.py文件

输入输出

  1. 输入: var = raw_input('prompt msg')
  2. 输出: 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

list可以增加删除元素,用[]框住;tuple元素确定后不可更改,用()框住
index都从0开始

条件判断和循环

  1. 条件判断
if 条件 : #注意冒号
    do something
if 条件 :
    do something
else :
    do some other things
if 条件:
    do something
elif 条件2 : #可以有多个elif
    do something
else :
    do some other things
  1. 循环
  • for ... in依次迭代list里的元素
names = ['a', 'b', 'c']
for name in names :
    print name

所以for x in ...循环就是把每个元素代入变量x,然后执行缩进块的语句。

  • while
while 条件 :
    do something

dict和set

  1. 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

  1. 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本身是不可变的。

对于不变对象来说,调用对象自身的任意方法,也不会改变该对象自身的内容。相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可变的。

函数

  1. 可以把库里面已有的函数名assign到一个变量里,然后用这个变量名做为函数来call
>>> a = abs
>>> a(-1)
1
  1. 自定义函数时有时需要定义默认参数,在定义参数时用等号定义:
def enroll(name, gender, age=6, city='Beijing'):
    print 'name:', name
    print 'gender:', gender
    print 'age:', age
    print 'city:', city

定义默认参数要牢记一点:默认参数必须指向不变对象!

  1. 参数类型:

可变参数既可以直接传入:func(1, 2, 3),又可以先组装list或tuple,再通过args传入:func((1, 2, 3));
关键字参数既可以直接传入:func(a=1, b=2),又可以先组装dict,再通过kw传入:func({'a': 1, 'b': 2})。

  1. 递归:

使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式,例如n * fact(n-1)。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。

切片

  1. python的index从0开始
  2. 几个例子:
>>> 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,返回值为表达式结果。

模块

  1. 表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释

  2. sys模块有一个argv变量,用list存储了命令行的所有参数。argv至少有一个元素,因为第一个参数永远是该.py文件的名称,例如:
    运行python hello.py获得的sys.argv就是['hello.py'];
    运行python hello.py Michael获得的sys.argv就是['hello.py', 'Michael']。

  3. 作用域

类似__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author____name__就是特殊变量,我们自己的变量一般不要用这种变量名; 类似_xxx__xxx这样的函数或变量就是非公开的(private),不应该被引用

  1. 调用module里的函数时先在python里import moduleName,然后用moduleName.functionName(var)

安装模块

  1. 以图片模块为例:
    安装命令: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', ...]

如果我们要添加自己的搜索目录,有两种方法:

  1. 一是直接修改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&instance

(抽象的)模板
实例 根据模板创建的具体对象

定义 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:
			returnAelif 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()获取属性值

⚠️ **GitHub.com Fallback** ⚠️