Python生态健全,在量化数据分析,机器学习等领域使用广泛
数据结构
元组与列表
已经有了列表这种数据结构,为什么还需要元组这样的类型呢?
元组中的元素是无法修改 的,多线程环境中可能更喜欢使用的是那些不变对象,一个不变的对象要比可变的对象更加容易维护;一个不变对象自动就是线程安全的,如果一个方法要返回多个值,使用元组也是不错的选择
元组在创建时间和占用的空间上面都优于列表。
输入输出函数
语法:
input([prompt])
prompt: 提示信息
Python3.x 中 input() 函数接受一个标准输入数据,返回为 string 类型
注意:input() 和 raw_input() 这两个函数均能接收 字符串 ,但 raw_input() 直接读取控制台的输入(任何类型的输入它都可以接收)。而对于 input() ,它希望能够读取一个合法的 python 表达式,即你输入字符串的时候必须使用引号将它括起来,否则它会引发一个 SyntaxError 。
除非对 input() 有特别需要,否则一般情况下我们都是推荐使用 raw_input() 来与用户交互。
注意:python3 里 input() 默认接收到的是 str 类型。
print函数
print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)
参数
objects – 复数,表示可以一次输出多个对象。输出多个对象时,需要用 , 分隔。
sep – 用来间隔多个对象,默认值是一个空格。
end – 用来设定以什么结尾。默认值是换行符 \n,我们可以换成其他字符串。
file – 要写入的文件对象。
flush – 输出是否被缓存通常决定于 file,但如果 flush 关键字参数为 True,流会被强制刷新。
返回值
无。
函数
在Python
中,函数的参数可以有默认值,也支持使用可变参数,所以Python没有重载的概念
作用域
可以使用global
关键字来指示foo函数中的变量a来自于全局作用域,如果全局作用域中没有a,那么下面一行的代码就会定义变量a并将其置于全局作用域。
同理,如果我们希望函数内部的函数能够修改嵌套作用域中的变量,可以使用nonlocal
关键字来指示变量来自于嵌套作用域
可变参数
可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple
1 2 3 4 5 def calc (*numbers ): sum = 0 for n in numbers: sum = sum + n * n return sum
关键字参数
关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict
1 2 3 4 5 def person (name, age, **kw ): print ('name:' , name, 'age:' , age, 'other:' , kw) >>> person('Michael' , 30 )name: Michael age: 30 other: {}
模块
导入的模块除了定义函数之外还有可以执行代码,那么Python解释器在导入这个模块时就会执行这些代码,如果我们在模块中编写了执行代码,最好是将这些执行代码放入如下所示的条件中,这样的话除非直接运行该模块,if条件下的这些代码是不会执行的,因为只有直接执行的模块的名字才是"__main__
"
面向对象
__init__
是一个特殊方法用于在创建对象时进行初始化操作
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Person : amount = 0 def __init__ (self,name,age,height ): self.name = name self.age = 25 self.height = height Person.amount +=1 def helloWorld (self ): print ("Hello World" ) def get_older (years ): self.age += years def __str__ (self ): return "Name:{},Age:{},Height:{}" .format (self.name,self.age,self.height) def __del__ (self ): Person.amount -= 1 print ("Object Deleted!" ) person1 = Person("Jack" ,30 ,180 ) print (person1.name)print (person1.age)print (person1.height)person1.name = "Henry" print (person1.name)person1.helloWorld() print (person1)person2 = Person("Sara" ,40 ,160 ) print (Person.amount)del person1print (Person.amount)
属性权限控制
在Python
中,属性和方法的访问权限只有两种,也就是公开的和私有的,如果希望属性是私有的,在给属性命名时可以用两个下划线作为开头
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Test : def __init__ (self, foo ): self.__foo = foo def __bar (self ): print (self.__foo) print ('__bar' ) def main (): test = Test('hello' ) test.__bar() print (test.__foo) if __name__ == "__main__" : main()
Python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来妨碍对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Test : def __init__ (self, foo ): self.__foo = foo def __bar (self ): print (self.__foo) print ('__bar' ) def main (): test = Test('hello' ) test._Test__bar() print (test._Test__foo) if __name__ == "__main__" : main()
@property装饰器
使用@property
包装器来包装getter和setter方法,使得对属性的访问既安全又方便
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 29 30 31 32 33 34 35 class Person (object ): def __init__ (self, name, age ): self._name = name self._age = age @property def name (self ): return self._name @property def age (self ): return self._age @age.setter def age (self, age ): self._age = age def play (self ): if self._age <= 16 : print ('%s正在玩飞行棋.' % self._name) else : print ('%s正在玩斗地主.' % self._name) def main (): person = Person('王大锤' , 12 ) person.play() person.age = 22 person.play() if __name__ == '__main__' : main()
_slots _
Python是一门动态语言,动态语言允许我们在程序运行时给对象绑定新的属性或方法,当然也可以对已经绑定的属性和方法进行解绑定。
限定自定义类型的对象只能绑定某些属性,可以通过在类中定义__slots__
变量来进行限定。需要注意的是__slots__
的限定只对当前类的对象生效,对子类并不起任何作用
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 29 30 31 32 33 34 class Person (object ): __slots__ = ('_name' , '_age' , '_gender' ) def __init__ (self, name, age ): self._name = name self._age = age @property def name (self ): return self._name @property def age (self ): return self._age @age.setter def age (self, age ): self._age = age def play (self ): if self._age <= 16 : print ('%s正在玩飞行棋.' % self._name) else : print ('%s正在玩斗地主.' % self._name) def main (): person = Person('王大锤' , 22 ) person.play() person._gender = '男'
静态方法和类方法
@staticmethod 静态方法
1 2 3 @staticmethod def is_valid (a, b, c ): return a + b > c and b + c > a and a + c > b
类方法的第一个参数约定名为cls,它代表的是当前类相关的信息的对象(类本身也是一个对象,有的地方也称之为类的元数据对象)
1 2 3 4 @classmethod def now (cls ): ctime = localtime(time()) return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec)
抽象类
Python从语法层面并没有像Java
或C#
那样提供对抽象类的支持,但是我们可以通过abc
模块的ABCMeta
元类和abstractmethod
包装器来达到抽象类的效果
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 29 30 31 32 from abc import ABCMeta, abstractmethodclass Pet (object , metaclass=ABCMeta): """宠物""" def __init__ (self, nickname ): self._nickname = nickname @abstractmethod def make_voice (self ): """发出声音""" pass class Dog (Pet ): """狗""" def make_voice (self ): print ('%s: 汪汪汪...' % self._nickname) class Cat (Pet ): """猫""" def make_voice (self ): print ('%s: 喵...喵...' % self._nickname) def main (): pets = [Dog('旺财' ), Cat('凯蒂' ), Dog('大黄' )] for pet in pets: pet.make_voice() if __name__ == '__main__' : main()
文件和异常
将那些在运行时可能会出现状况的代码放在try
代码块中,在try代码块的后面可以跟上一个或多个except
来捕获可能出现的异常状况,避免执行时程序的异常退出。
1 2 3 4 5 6 try : x = int (input ("First number:" )) y = int (input ("Second number:" )) print (x / y) except : print ("There was an error!" )
First number:10
Second number:0
There was an error!
如果不用try except 除数为0时会直接终止程序,精准捕获错误类型
1 2 3 4 5 6 7 8 try : x = int (input ("First number:" )) y = int (input ("Second number:" )) print (x / y) except ValueError: print ("Please enter a valid number next time!" ) except ZeroDivisionError: print ("Cannot divide by zeor!" )
1 2 3 4 5 6 7 First number:10 Second number0 Cannot divide by zeor! ================= RESTART: /Users/jack/Desktop/python/hello.py ================= First number:text Please enter a valid number next time!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def main (): f = None try : f = open ('致橡树.txt' , 'r' , encoding='utf-8' ) print (f.read()) except FileNotFoundError: print ('无法打开指定的文件!' ) except LookupError: print ('指定了未知的编码!' ) except UnicodeDecodeError: print ('读取文件时解码错误!' ) finally : if f: f.close() if __name__ == '__main__' : main()
读写json文件
把一个列表或者一个字典中的数据保存到文件中又该怎么做呢?答案是将数据以JSON格式进行保存,json应用于跨平台跨语言的数据交换,原因很简单,因为JSON也是纯文本,任何系统任何编程语言处理纯文本都是没有问题的
json模块主要有四个比较重要的函数,分别是:
dump - 将Python对象按照JSON格式序列化到文件中
dumps - 将Python对象处理成JSON格式的字符串
load - 将文件中的JSON数据反序列化成对象
loads - 将字符串的内容反序列化成Python对象
进程和线程
Python既支持多进程又支持多线程
多进程
Unix和Linux操作系统上提供了fork()系统调用来创建进程,调用fork()函数的是父进程,创建出的是子进程
fork()函数非常特殊它会返回两次,父进程中可以通过fork()函数的返回值得到子进程的PID,而子进程中的返回值永远都是0
由于Windows系统没有fork()调用,因此要实现跨平台的多进程编程,可以使用multiprocessing
模块的Process
类来创建子进程
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 rom multiprocessing import Process from os import getpidfrom random import randintfrom time import time, sleepdef download_task (filename ): print ('启动下载进程,进程号[%d].' % getpid()) print ('开始下载%s...' % filename) time_to_download = randint(5 , 10 ) sleep(time_to_download) print ('%s下载完成! 耗费了%d秒' % (filename, time_to_download)) def main (): start = time() p1 = Process(target=download_task, args=('Python从入门到住院.pdf' , )) p1.start() p2 = Process(target=download_task, args=('Peking Hot.avi' , )) p2.start() p1.join() p2.join() end = time() print ('总共耗费了%.2f秒.' % (end - start)) if __name__ == '__main__' : main()
也可以使用subprocess
模块中的类和函数来创建和启动子进程,然后通过管道来和子进程通信
两个进程间的通信
使用multiprocessing
模块中的Queue
类,它是可以被多个进程共享的队列,底层是通过管道和信号量(semaphore
)机制来实现的
多线程
多线程开发推荐使用threading
模块
Python的多线程并不能发挥CPU的多核特性,之所以如此,是因为Python的解释器有一个“全局解释器锁”(GIL)的东西,任何线程执行前必须先获得GIL锁,然后每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行,这是一个历史遗留问题
多进程还是多线程
是否采用多任务的第二个考虑是任务的类型,可以把任务分为计算密集型和I/O密集型
计算密集型任务由于主要消耗CPU资源,这类任务用Python这样的脚本语言去执行效率通常很低,最能胜任这类任务的是C语言,Python中有嵌入C/C++
代码的机制。
网络、存储介质I/O的任务都可以视为I/O密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待I/O操作完成(因为I/O的速度远远低于CPU和内存的速度),对于I/O密集型任务,如果启动多任务,就可以减少I/O等待时间从而让CPU高效率的运转
单线程 + 异步I/O
现代操作系统对I/O操作的改进中最为重要的就是支持异步I/O。如果充分利用操作系统提供的异步I/O支持,就可以用单进程单线程模型来执行多任务,这种全新的模型称为事件驱动模型。Nginx就是支持异步I/O的Web服务器,它在单核CPU上采用单进程模型就可以高效地支持多任务。在多核CPU上,可以运行多个进程(数量与CPU核心数相同),充分利用多核CPU。用Node.js开发的服务器端程序也使用了这种工作模式,这也是当下并发编程的一种流行方案。
在Python语言中,单线程+异步I/O的编程模型称为协程
,有了协程的支持,就可以基于事件驱动编写高效的多任务程序。协程最大的优势就是极高的执行效率,因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销。协程的第二个优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不用加锁,只需要判断状态就好了,所以执行效率比多线程高很多。如果想要充分利用CPU的多核特性,最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。
yield关键字
一个带有 yield 的函数就是一个 generator
,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行
如何判断一个函数是否是一个特殊的 generator
函数?可以利用 isgeneratorfunction
判断:
1 2 3 >>>from inspect import isgeneratorfunction >>> isgeneratorfunction(fab) True
参考
Python入门到精通
Python 教程