本文根据廖雪峰的博客,学习整理笔记。主要内容有:基本数据类型,容器数据类型,变量及其作用域,判断及循环语法,函数式编程,面向对象,模块等概念。

数据类型

在python中,能够直接处理的数据类型有以下几种:

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。
  • 整数
  • 浮点数
  • 字符串
  • 布尔值
  • 空值

其他内置的数据类型:

  • 列表 list 是一种有序的集合,例如 classmates = ['Michael', 'Bob', 'Tracy']
    • len() 获取个数
    • list[0] 取值
    • apend('tom') 追加元素到末尾
    • insert(1,'tom') 插入指定元素 
    • pop(1) 删除指定位置元素,不传参数删除末尾元素
  • 元祖 tuple 和list类似,一旦初始化就不能修改,例如 classmates = ('Michael', 'Bob', 'Tracy')
    • 只有一个元素的元祖可以表示为 t = (1,)
    • 没有append(),insert()这样的方法
  • 字典 dict 全称dictionary,类似java中map,使用键值对存储,例如 d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
    • 取值 d['Michael']
    • 判断key值是否存在,通过in判断,'Tom' in d, 或者get(),不存在返回None
    • 删除一个key,用pop(key)方法,对应的value以会删除
    • dict的key必须是不可变对象,在python中字符串、整数等都是不可以变的,可以作为key。而list不可变,不能作为key
  • set 和list类似,但是没有重复的元素,set的原理和dict一样,set存放的是key,没有value。要创建一个set,需要提供一个list作为输入集合,例如 s = set([1, 2, 3]) ,打印出的结果是 {1,2,3}
    • add(key) 添加元素
    • remove(key) 删除元素

变量

这种变量本身类型不固定的语言称之为动态语言,与之对应的是静态语言。静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错。

字符串和编码

现在计算机系统通用的字符编码工作方式:在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。

由于Python的字符串类型是str,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str变为以字节为单位的bytes。

Python对bytes类型的数据用带b前缀的单引号或双引号表示。

函数

判断:

if case1:
    do1
elif case2:
    do2
elif case3:
    do3
else:
    do4

循环:python中循环有两种,一种是for...in循环,依次把list或tuple中每个元素迭代出来;第二种是while循环

names = ['Michael', 'Bob', 'Tracy']
for name in names:
    print(name)

python内置函数range(),可以生成一个整数序列,在通过list()函数可以转换为list。例如 list(range(5))

sum = 0
n = 99
while n > 0:
    sum = sum + n
    n = n - 2
print(sum)

在循环中,break语句可以提前退出循环,continue可以跳过当前这次循环。

 

计算机程序中,函数就是最基本的一种代码抽象的方式。

函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”,例如:

>>> a = abs # 变量a指向abs函数
>>> a(-1) # 所以也可以通过a调用abs函数
1

定义函数,使用def语句,依次写出 函数名 括号 括号中的参数 冒号,然后,在缩进快中编写函数体,函数的返回值用return语句返回。如果没有return语句,函数执行完毕后也会返回结果,只是结果为None。retrun None可以简写为return。

def my_abs(x):
    if x >= 0:
        return x
    else:
        return -x

空函数:如果想定义一个什么事也不做的空函数,可以用pass语句:

def nop():
    pass

参数检查:调用函数时,如果参数个数不对,Python解释器会自动检查出来,并抛出TypeError。数据类型检查可以用内置函数isinstance()实现。

返回多个值:可以返回多个值,但这其实只是一种假象,返回值是一个tuple,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值。例如:

import math

def move(x, y, step, angle=0):
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)
    return nx, ny

函数的参数:

默认参数,默认参数必须指向不变对象

def power(x, n=2):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

可变参数,在参数前面加了一个*号,参数numbers接收到的是一个tuple,调用该函数时,可以传入任意个参数

def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum

关键字参数,关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict

def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)

# 调用后
>>> person('Adam', 45, gender='M', job='Engineer') name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}

# 复杂调用
>>> extra = {'city': 'Beijing', 'job': 'Engineer'} >>> person('Jack', 24, city=extra['city'], job=extra['job']) name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}
# 简化后,**extra表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数,
# kw将获得一个dict,注意kw获得的dict是extra的一份拷贝,对kw的改动不会影响到函数外的extra
>>> extra = {'city': 'Beijing', 'job': 'Engineer'} >>> person('Jack', 24, **extra) name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

 命名关键字参数,如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。和关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。

def person(name, age, *, city, job):
    print(name, age, city, job)

# 调用方式

>>> person('Jack', 24, city='Beijing', job='Engineer') Jack 24 Beijing Engineer

 参数组合,在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。

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)

 

函数式编程

函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。

函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。

函数名其实就是指向函数的变量,既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数(Higher-order function)。

def add(x, y, f):
    return f(x) + f(y)

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。相关参数和变量都保存在返回的函数中,这种程序结构称为”闭包(Closure)”。

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

匿名函数:关键字体lamba表示匿名函数。

装饰器:在函数调用前后对函数进行增强,但是又不希望修改原函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

import functools


def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print('%s %s' % (text, func.__name__))
            nest = func(*args, **kwargs)
            print('end call')
            return nest
        return wrapper
    return decorator


@log('execute')
def now(who):
    print(who + ' 2015-3-25')


if __name__ == "__main__":
    now('System say')

python的@语法,可以实现函数的增强功能。 

生成器:可以参考这篇文章https://www.cnblogs.com/wongbingming/p/9060989.html

模块

为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。在Python中,一个.py文件就称之为一个模块(Module)。

为了避免模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Package)。

请注意,每一个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。__init__.py可以是空文件,也可以有Python代码,因为__init__.py本身就是一个模块,而它的模块名就是包名。

一个hello模块:

 1 #!/usr/bin/env python3
 2 # -*- coding: utf-8 -*-
 3 
 4 ' a test module '
 5 
 6 __author__ = 'Michael Liao'
 7 
 8 import sys
 9 
10 def test():
11     args = sys.argv
12     if len(args)==1:
13         print('Hello, world!')
14     elif len(args)==2:
15         print('Hello, %s!' % args[1])
16     else:
17         print('Too many arguments!')
18 
19 if __name__=='__main__':
20     test()

第1行和第2行是标准注释,第1行注释可以让这个hello.py文件直接在Unix/Linux/Mac上运行,第2行注释表示.py文件本身使用标准UTF-8编码;

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

第6行使用__author__变量把作者写进去,这样当你公开源代码后别人就可以瞻仰你的大名;

后面就开始是真正的代码部分。使用sys模块的第一步,就是导入该模块,第8行。

第19行和第20行的意思是,Python解释器把一个特殊变量__name__置为__main__,而如果在其他地方导入该hello模块时,if判断将失败,因此,这种if测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试。

变量和函数作用域:

  • 私有的 private 通过下划线_前缀来实现
  • 类似 __xxx__ 这样的变量是特殊变量,可以被直接引用,但是有特殊用途,一般我们自己的变量不要用这种变量名
  • 公开的 pulic 正常的函数和变量名

安装第三方模块:一般来说,第三方库都会在Python官方的pypi.python.org网站注册,要安装一个第三方库,必须先知道该库的名称,可以在官网或者pypi上搜索。然后用包管理工具pip完成安装,例如 pip install package

面向对象

在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。

面向对象的设计思想是从自然界中来的,因为在自然界中,类(Class)和实例(Instance)的概念是很自然的。Class是一种抽象概念,比如我们定义的Class——Student,是指学生这个概念,而实例(Instance)则是一个个具体的Student,比如,Bart Simpson和Lisa Simpson是两个具体的Student。

在Python中,定义类是通过class关键字:

class Student(object):

    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s: %s' % (self.name, self.score))

由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法,在创建实例的时候,就把name,score等属性绑上去。

注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。

有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去。

和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数。

可以使用type()函数来判断对象类型。

对于class的继承关系来说,使用type()就很不方便。我们要判断class的类型,可以使用isinstance()函数。

如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list。

如果想要限制实例属性,可以在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性。

Python中可以通过@property和@param.setter这个两个装饰器将普通方法变成属性调用,用起来和get/set一样。

Python中可以有多重继承,这种设计通常称之为 MixIn 。Java中不支持多继承,但是可以实现接口。

Python的class中有许多有特殊用途的函数,可以帮助我们定制类。

  • __str__
  • __iter__
  • __getitem__
  • __getattr__
  • __call__

枚举:可以从Enum派生出自定义类,例如

from enum import Enum, unique


@unique
class Weekday(Enum):
    Sun = 0 # Sun ? value ???? 0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6


if __name__ == '__main__':
    print(Weekday.Wed.value)

 

@unique装饰器可以帮助我们检查保证没有重复值

异常和错误

错误处理机制

try:
    print('try...')
    r = 10 / 0
    print('result:', r)
except ZeroDivisionError as e:
    print('except:', e)
finally:
    print('finally...')
print('END')

补充概念

可迭代、迭代器、生成器三者区别:

  • 可迭代对象可以使用for循环。其内部实现了 __iter__ 这个魔术方法。例如:字符串、list、tuple、list等。
  • 迭代器对比可以迭代对象,多了一个函数 __next__ 这样我们既可以使用for循环来间断获取元素值,也可以直接使用next()方法来实现。
  • 生成器则是在迭代器的基础上(可以用for循环,可以用next()),在实现了yield

yield可以理解为函数中的return。在每次next(),或者for遍历的时候,都会yield这里将新的值返回回去,并在这里阻塞,等待下一次的调用。

创建一个生成器有如下两种方法:

  • 使用列表生成式
  • 实现yield的函数

可迭代对象和迭代器,是将所有的值都生成存放在内存中;而生成器则是需要元素才临时生成,节省时间,节省空间。

from collections.abc import Iterable, Iterator, Generator

# 列表生成器
L = (x * x for x in range(10))
print(L)
print(isinstance(L, Iterable))
print(isinstance(L, Iterator))
print(isinstance(L, Generator))


# 实现yield的函数
def mygen(n):
    now = 0
    while now < n:
        yield now
        now += 1
gen = mygen(4)
print(next(gen))
print(gen.send(None))
print(next(gen))
print(gen.send(None)) 

由于生成器并不是一次生成所有元素,而是一次一次的执行返回,那么如何刺激生成器执行(或者说激活)呢?激活主要有两个方法:

  • 使用 next(generator)
  • 使用 generator.send(None)

生成器在其生命周期中,会有如下四个状态:

  • GEN_CREATED # 等待开始执行
  • GEN_RUNNING # 解释器正在执行(只有在多线程应用中才能看到这个状态)
  • GEN_SUSPENDED # 在yield表达式处暂停
  • GEN_CLOSED # 执行结束

生成器在工作过程中,若生成器不满足生成元素的条件,就会应该抛出异常(StopIteration),我们在自定义生成器的时候,在不满足生成元素时,抛出异常。

from inspect import getgeneratorstate


def mygen(n):
    now = 0
    while now < n:
        yield now
        now += 1
    raise StopIteration


if __name__ == '__main__':
    gen = mygen(2)
    print(getgeneratorstate(gen))

    print(next(gen))
    print(getgeneratorstate(gen))

    print(next(gen))
    gen.close()  # 手动关闭/结束生成器
    print(getgeneratorstate(gen))

如何向生成器发送消息,就是用send(100)方法:

def jumping_range(N):
    index = 0
    while index < N:
        # 通过send()发送的信息将赋值给jump
        jump = yield index
        if jump is None:
            jump = 1
        index += jump


if __name__ == '__main__':
    itr = jumping_range(5)
    print(next(itr))
    print(itr.send(2))
    print(next(itr))
    print(itr.send(None))

上面的例子中,通过yield将函数分成各部分:

  • yield index 是将index return给外部调用程序
  • jump = yield 可以接收外部程序通过send()发送的信息,并赋值给jump

协程:为非抢占式多任务产生子程序的计算机程序组件,协程允许不同入口点在不同位置暂停或开始执行程序。

子程序,或者称为函数,在所有语言中都是层级调用,例如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。子程序调用是通过栈实现的,一个线程就是执行一个子程序。子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。

yield from 后面需要加的是可迭代对象,它可以是普通的可迭代对象,迭代器,或者生成器。

asyncio 是python3.4版本引入的标准库,直接内置了对异步IO的支持。

和其相关的概念:

  • event_loop 事件循环:程序开启一个无限的循环,程序员会把一些函数(协程)注册到事件循环上。当满足事件发生的时候,调用相应的协程函数。
  • coroutine 协程:协程对象,指一个使用async关键字定义的函数,它的调用不会立即执行函数,而是会返回一个协程对象。协程对象需要注册到事件循环,由事件循环调用。
  • future 对象: 代表将来执行或没有执行的任务的结果。它和task上没有本质的区别
  • task 任务:一个协程对象就是一个原生可以挂起的函数,任务则是对协程进一步封装,其中包含任务的各种状态。Task 对象是 Future 的子类,它将 coroutine 和 Future 联系在一起,将 coroutine 封装成一个 Future 对象。
  • async/await 关键字:python3.5 用于定义协程的关键字,async定义一个协程,await用于挂起阻塞的异步调用接口。其作用在一定程度上类似于yield。

 

python代码规范 https://www.python.org/dev/peps/pep-0008/

END

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄