Python学习笔记

写在前面

每种语言都有各自的特性,适用的场景也有差别,比如C/C++常用于高性能环境中,而Java常用于网络应用程序,Python给人的感觉有点想Shell等脚本语言,能快速开发出程序,关键是在Windows、Linux、Mac上基本上都是有安装Python的。

这篇是学习笔记,主要用来记录在用Python编程时需要注意的地方。下面来看一些非常常用的操作:

isinstance:判断obj是否为对应类型的实例

type:获取obj的类型

dir:获取所有的属性和方法

callable:对象是否可以执行

基础

字符串编码问题

在python 2.x中使用的str实际是字节码,是Unicode经过编码后的字节组成的序列,简单的验证方法是通过print len('我')输出3(utf-8编码中汉字有三个字节)得到验证,str和Unicode都是basestring的子类,而Unicode才是真正意义上的字符串,即printf len(u'我')的结果为1。

另外在有中文的python源文件上有加注释来说明编码:# coding: UTF-8

在python中打开文件读取到的内容实际上是str,而不是unicode(这样实现更合理),如果要当做字符串处理的话要自己encode。

数据集合

提供了list(可变有序列表)、tuple(不可变的列表)、dict(字典)、set(集合)等能快速使用的数据结构,在根据下标访问时传入负数不会报错,比如list[-1]在实际上得到的是最后一个元素。在初始化时集合中的数据可以是任意的,不需要一致:

s = ['python', 'java', ['asp', 'php'], 'scheme']

在python中有很多截取、生成列表的例子,这也算是一个比java、c灵活快捷的地方:

[x * x for x in range(1, 11)]                  # 从1到10的平方的数组
[x * x for x in range(1, 11) if x % 2 == 0]    # 可以增加条件
[m + n for m in 'ABC' for n in 'XYZ']          # 甚至嵌套循环

list[0:3]    # 取list前3个元素
list[:3]     # 同上,第一个是0则可以省略不写
list[-3:]    # 取list后3个元素
list[::3]    # 每3个取一个
list[:]      # 复制一个list

这样生成的数据是占用内存的,有时候这样的代价太大,这便有了generator,它只保存了逻辑,不存储数据。generator的初始化方法只是简单的将上面的中括号变成括号即可:

g = (x * x for x in range(10))

而在访问generator是使用g.next()来完成,当然也可以使用for...in...的方式。

函数和逻辑控制

在python中比较蛋疼的是通过缩进来控制逻辑控制语句、函数的范围,写不好就错了,比如if语句你要写成:

if a > 0 :
    print 1
    print 2 # 如果这一行没有缩进的话a < 2也能输出来了

和c的函数指针有点类似的是python中的函数也可以传递,比如在用sorted对列表实现倒序排序时将比较函数当做参数传进去:

def cmp(x, y) : 
    return y - x;
sorted([1, 2, 3, 4], cmp);

同样的方法也可以作为返回值。lambda函数使得这种用法更爽(虽然python对lambda的支持有限,只有在简单情况下此案呢过使用),上面三行有一行就可以搞定了:

sorted([1, 2, 3], lambda x,y:y-x);

调用函数的地方传递参数并不一定要跟函数声明时的顺序保持一致,只要你讲清楚具体是哪个传给哪个就可以了,举个例子,申明一个函数为func(a, b),在调用是可以是:

func(b=1,a=2) 等价于 func(2, 1)

在很多的python代码中会看到func(*args, **kwargs)类型的参数,那么可以猜到第一个为正常方式传入的,第二种为key-value方式传入的。在参数比较多的函数调用时比较麻烦,因为大部分参数在大部分场景下是相同的,在Java/C中的方法是重新声明一个函数,而在python中进一步简化(需要functools模块):

int2 = functools.partial(int, base=2)

另外Spring中的AOP很好用吧?这里也有了类似的写法:

def log(func):
    def wrapper(*args, **kw):
        print 'call %s():' % func.__name__
        return func(*args, **kw)
    return wrapper

@log
def now():
    print '2013-12-25'

模块

把所有的代码写在一个文件里面很不显示,python通过模块的方式来组织,声明的方法如下:

mycompany(一个模块就是一个目录

__init__.py让解释器将该目录当成模块

xxx.py(模块中的内容

用import引入之后就可以使用xxx功能了。

面向对象编程

在申明类型时跟函数一样,需要用缩进来标记出来属于该类的代码的范围,最简单的例子:

class Student :
	def __init__(self, name, score): # 构造函数
		self.name = name
		self.score = score

在使用时s = Student()即可得到一个实例,python中的继承与Java/C++稍有不同:class Student(Person) 。类是属性和方法的封装体,自然并不是所有的属性都能被访问,在python中比较“牛逼”的地方是用两个下划线来将属性保护起来:

s.__name

访问的时候会出错~ 使用时可以“随意”地给类型、实例添加属性和方法,给obj添加属性很简单:s.age=18,给class添加方法之后,所有的实例中都可以访问了:

Student.set_score = MethodType(set_score, None, Student)

很多时候我们并不希望大家随意地添加属性,那么可以通过__slots__ = ('name', 'age')来进行限制,需要注意的是该方法对子类并不起作用。用函数的方式设置和读取看起来比较麻烦,在python又做了一次改进,通过注解将对属性的访问映射到函数上:

class Student(object):
    @property
    def score(self):
        return self._score
    @score.setter
    def score(self, value):
	self._score = value

从次可以用s.score=2来调用score(self,value)方法,这个想法很不错,但是貌似这样写完会把代码搞乱掉的,让调用者看得云里雾里。为了将输出更优化在Java中建议重写toString方法,同样目的,在python中建议重写__str____repr__方法,另外可以做出一些额外的定制:

__getattr__:从obj中获取不到属性时,会尝试调用该方法,如果不重写的话会直接抛出AttributeError

__call__:使obj可以像函数一样执行

__getitem__:用来支持obj[0]这种下标的方式获取数据

__iter__:用来支持for .... in这种循环遍历

后面两种有点像C++里面的运算符重载,感觉应用的场景很少。在Java中可以使用cglib等产生新的类型,Python中的type()也提供了类似的功能:

type('Hello', (object,), dict(hello=fn))

其中,第一个参数是class的名称,第二个参数是父类的集合,第三个参数是将方法名称(hello)和函数(fn)绑定。另外一种创建类型的方法是metaclass,来看一个例子,看不懂就算了:

# metaclass是创建类,所以必须从`type`类型派生:
class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)

class MyList(list):
    __metaclass__ = ListMetaclass # 指示使用ListMetaclass来定制类

在用到对象的地方一般都会涉及到序列化,python通过pickle.dumpspickle.loads来完成序列化和反序列化,还有问题是对象与JSON格式的字符串进行互相转换,可以使用json.dump来变成JSON,不过它并不认识你自己定义的类型,需要用指定一个函数来完成,简单做法如下:

print(json.dumps(s, default=lambda obj: obj.__dict__))

而在从JSON变成对象的时候需要指定object_hock方法用来根据dict生成对象:

json.loads(json_str, object_hook=dict2student)

进程、线程、协程

创建进程与C一样:os.fork(),如果是父进程的话返回子进程ID,如果是子进程的话返回0。另外还可以通过multiprocessing模块创建进程:

Process(target=run_proc, args=('test',))

如果要创建大量子进程可以用Pool的方式批量创建:

p = Pool()
for i in range(5):
        p.apply_async(long_time_task, args=(i,))

进程间的通信可以通过QueuePipes来做。Python进程的一个牛逼之处是可以使用managers模块方便地做分布式进程。

Python中的线程是真正的Posix Thread,但不幸的是解释器执行代码时有一个GIL锁,任意Python线程执行前必须获取GIL锁,导致的结果就是多线程不能有效地利用多核优势,不管怎么样看下线程的用法:

t = threading.Thread(target=run_thread, args=(5,))  # 创建线程
t.start()  # 开始执行
lock = threading.Lock()  # 创建锁
lock.acquire()  # 获取锁
// todo
lock.release()  # 释放锁

Java中的ThreadLocal很好用吧,Python也有(感觉都是抄来抄去),用法基本上一样:

local_school = threading.local()  # 创建ThreadLocal变量
local_school.student = 123  # 绑定属性

其他的基础方面的还有网络编程,但是这部分和Java/C太像了,这里就不写了。

数学计算

毫无疑问在搞机器学习计算的最好工具是Matlab,但是作为一个程序员比较排斥这种东西,更愿意自己写代码来做,但是不用现成的模块的话代码量巨大,所以这里介绍几种常用的Python中数值计算的工具。

NumPy

该模块可以搞定大部分的矩阵中的运算,在官网中的文档看起来要比大部分的翻译轻松很多,它所支持的功能可以在这里查看,如果访问不了可以试试这个

SciPy

用来支持最优化、线性代数、积分、插值等,和NumPy的有一定的重复,在这里可以用具体用法,如果访问不了可以试试这个

SymPy

纯python实现的符号运算器,感觉很强大,但是我实际上还没用到,因为都是自己在推公式,感兴趣可以在官网了解。

matplotlib

一个画图工具,为什么需要画图?你运算的结果或者模型在你脑子里面是清楚的,但是你要跟人家展示啊,官网学习中。

实战

原文地址:https://www.cnblogs.com/antispam/p/4018452.html