admin 管理员组

文章数量: 887031

Python 教程

本教程以最简单的方式力求全面介绍常用和常见python语法及相关特性。

本教程适用于有一定的编程(C/C++/Java/C#/js等)基础的人员。

作者-dwSun

Python介绍与安装

历史

Python的创始人为Guido van Rossum。1989年圣诞节期间,在阿姆斯特丹,Guido为了打发圣诞节的无趣,决心开发一个新的脚本解释程序,作为ABC 语言的一种继承。之所以选中Python(大蟒蛇的意思)作为该编程语言的名字,是因为他是一个叫Monty Python的喜剧团体的爱好者。

Python本身是一门通用编程语言,借助各种功能强大的扩展库(numpy, scipy, matplotlib) ,python成为了科学计算领域不可或缺的一种必备技能。

同时随着AI领域的发展,以Python作为API interface的各种AI框架的流行,Python成为了从事AI研发领域的人员必备技能之一。

Python2/3选择

由于历史原因,Python目前有2.x和3.x两个主要版本正在发行,2.x和3.x的语法有一些细节不同。这导致两个版本的代码很多时候不通用。

曾经,启动Python项目的时候,一般都面临版本选择的问题。如果是需要在一些比较陈旧的系统环境上运行,抑或要使用一些特殊的库,而同时这些库又没有python3版本的情况下,就需要使用python2.x系列。如果要使用一些比较新的库,那么python3.x是一个比较好的选择。

版本的选择,让很多初学者无所适从。同时支持两个版本系列,也让各扩展库的开发者耗费了大量的精力。

基于此种状况,Python社区决定,自2020年1月1日,python2.x系列不再提供社区支持。同时,很多扩展库,如numpy,matplotlib等,也宣布不再继续支持python2.x系列。

因此python3.x成为了目前最直接的选择。本教程不再介绍python2.x语法。

值得一提的是,python2.x只是不再提供社区支持,付费商业支持还是有提供的,所以已经存在的商业软件,不受影响,但社区仍然推荐尽快切换到python3.x系列。

3.x版本选择

此教程编写的时候,官网能下载到的最新python版本是3.7.3,目前常用的各个科学计算库以及AI框架也都支持3.7系列。因此不管在哪个系统上安装,直接装最新版本即可。

如果python3.8发布,那么发布时刻短时间内,各框架未适配的情况下,仍然需要安装3.7版本的Python。这种情况请区别对待。

编辑器/IDE选择

各种编辑器,IDE,各有千秋,dwSun建议初学者先用着python自带的IDLE,等熟悉语言之后再考虑用哪个编辑器。

这里为了课程组织方便,IDE使用的是JupyterLab,但是JupyterLab本身不太适合初学者,使用比较复杂。

dwSun认为比较好用,有一定参考价值的IDE列举如下:
https://www.tinymind/articles/4005

win/mac/linux的python下载安装

不管是在哪个环境下,python的安装都有三种常用的不同的方式可以选择:

  • 源代码编译安装
  • 原生安装
    • win下安装包安装
    • mac下安装包安装
    • linux下通过包管理器安装
  • anaconda/miniconda安装

源代码编译安装

此种安装方式最不推荐,仅在确实必要的情况下使用此种安装方式。

编译安装,由于可以使用目标平台的特定指令集,所以性能会有一定的提升(经验值在10%~30左右)。

原生安装

对于初学者来说,这是最推荐的安装方式,其他安装方式,都会引入其他比较复杂的步骤,比较适合有一定经验的用户

win下安装包安装

以3.7.3为例,去https://www.python/downloads/release/python-373/ 下载“Windows x86-64 executable installer”安装包名称为python-3.7.3-amd64.exe,下载后双击安装即可。
需要注意,安装过程中,一定要勾选PIP(Python包管理器),IDLE(Python自带的IDE)安装选项。同时为了使用方便,一定要勾选 “Add to PATH”选项。

mac下安装包安装

以3.7.3为例,去https://www.python/downloads/release/python-373/ 下载“macOS 64-bit installer”安装包名称为python-3.7.3-macosx10.9.pkg,下载后双击安装。
需要注意,安装过程中,一定要勾选PIP(Python包管理器),IDLE(Python自带的IDE)安装选项。同时为了使用方便,一定要勾选 “Add to PATH”选项。

注意,Mac自带了python2.x版本,使用的时候一定注意启动的是哪个版本。

linux下通过包管理器安装

以ubuntu18.04为例,直接执行以下命令安装即可:

sudo apt install python3 python3-pip

anaconda/miniconda安装

Anaconda是一个开源的包、环境管理器,可以用于在同一个机器上安装不同版本的软件包及其依赖,并能够在不同的环境之间切换,因为包含了大量的科学包,Anaconda 的下载文件比较大(500 MB以上),如果只需要某些包,或者需要节省带宽或存储空间,也可以使用Miniconda这个较小的发行版(仅包含conda和 Python)。

anaconda/miniconda不仅可管理python的版本,还能管理其他一些科学计算库,是一个通用的环境管理器。

可以去https://www.anaconda/distribution/#download-section 下载windows/mac/linux版本的安装 包进行安装。
请到https://docs.anaconda/anaconda/install/ 下查找相关安装文档。

miniconda请到https://docs.conda.io/en/latest/miniconda.html 下载,安装文档请参考https://conda.io/projects/conda/en/latest/user-guide/install/index.html

anaconda安装包过大,使用也比较复杂,推荐有一定经验的用户使用。miniconda作为anaconda的一个基础版本,安装包比较小,使用也比较灵活,相较与anaconda,本文作者更推荐使用miniconda。

anaconda/miniconda安装后,会自动设置系统的环境变量,默认使用自带的python,这在系统环境比较复杂的情况下可能会导致一些严重问题,请一定注意这种情况

win/mac/linux的python下使用

python console与cmd/shell

安装完成之后,可以直接在命令行/terminal中执行python3,进入python shell,不管在哪个系统上,都可以看到类似下面的提示:

Python 3.7.3 (default, Apr  3 2019, 05:39:12) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 

windows和mac上面,一定注意安装的时候,勾选 “Add to PATH”选项。

这里需要注意的是,执行完python3程序进入的python shell,只能输入python代码,不能执行windows下cmd里面的命令或者mac/linux下terminal命令(如 ls ,mv,rm,cp等命令都是命令行执行,不是python代码)。

也可以通过IDLE启动python,各个系统上启动IDLE方式不太一样,这里不再赘述。

IDLE 自带python shell和一个简单的文本编辑器,虽然功能很有限,但是已经足够日常使用。

py文件

python shell比较适合小段代码或者临时使用,代码比较多的话,推荐写到一个py文件里面。

一个py文件一般以下述代码作为开始:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

其中第一行为解释器声明,必须是文件的第一行,前面不能有空行,这个在linux系统上才会用到,但是推荐所有py代码都加上这一句。

解释器声明也有一些不太一样的写法,例如 #!/usr/bin/python3这种的,dwSun这里给的写法是我自己用的比较多的。

第二行是文件编码声明,如果不写这一行的话,python的interpreter默认用ascii解码py文件并执行,这个时候,如果你的代码里面包含中文字符,会出现解码错误。
dwSun个人推荐所有py文件开头都加上这两行。

py文件可以直接用python程序执行,作为参数直接传递给python就可以。

python file.py

另外,IDLE之类的python编辑器/IDE都有运行代码的功能,写好了代码之后,可以直接在相应的编辑器/IDE里面点击某个按钮运行,每个编辑器/IDE的功能不太一样,这里不再赘述。

pip使用

前文提到,python拥有庞大的生态系统支持,数量众多质量优良的各种扩展库极大的扩充了python的能力,让python成为了各个领域的不二之选。包的安装和使用也是python的必备技能之一。

pip是python自己的包管理器,pypi则是python官方的包仓库,其官方网址为https://pypi/ ,截止本文编写之时,pypi仓库里面已经有179950个项目,可以为pyton提供功能上的扩展。

pypi仓库服务器本身位于海外,访问速度比较慢,为了解决这个问题,可以使用tuna,ali,163,中科大等的国内pypi镜像,具体步骤可以666,有很多相关资料,各镜像源也有相关的配置文档,这里不再赘述。

本教程中,python安装的时候,就已经要求读者安装pip工具,所以后续内容默认pip已经安装。
常用命令如下:

pip install numpy # 安装numpy
pip install numpy==1.14.6 # 安装1.14.6版本的numpy
pip install -U numpy # 安装或者升级numpy
pip uninstall numpy # 卸载numpy
pip search numpy # 在pypi中查找numpy
pip --help # 显示帮助文档
pip -V # 显示版本信息
pip install -r requirements.txt # 一个个安装太麻烦了,可以把所有的库和相应版本都写到requirements.txt文件中,一次性安装,特别适合项目环境配置。

如果系统中同时安装有python2.x python3.x,则有可能出现pip/pip2/pip3等不同的名字的pip,这时,pip2一定是python2.x版本的,pip3一定是python3.x版本的,但是pip不一定是哪个版本,请一定要注意此类问题。

由于pip安装库的时候,需要联网下载相关库和依赖库的安装文件,所以请一定保证网络流畅可用,或者可以选择使用国内的pypi仓库镜像。

Python基础语法

Python是一门动态强类型语言,由于其代码在拥有强大的程序表达能力的同时,还具备高度的可读性,因此很多时候被称为一门类伪代码的语言。下面是一个python代码的快速排序例子。仅作为示例,这里不做展开。

def quicksort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[len(arr) // 2]
    left = [x for x in arr if x < pivot]
    middle = [x for x in arr if x == pivot]
    right = [x for x in arr if x > pivot]
    return quicksort(left) + middle + quicksort(right)


print((quicksort([3, 6, 8, 10, 1, 2, 1])))
[1, 1, 2, 3, 6, 8, 10]

缩进

与C语言,Java等语言使用大括号来标记语法边界不同,python中强制使用缩进来标记代码的语法边界 ,拥有同一缩进层级的代码属于同一个语法层次。

缩进可以使用tab,也可以使用空格,因为tab在各个编辑器上定义的宽度不同,所以为了代码的可读性,推荐使用空格进行代码的缩进。需要注意的是,虽然python规定必须使用缩进来标记语法边界,但是并未规定使用多少个字符作为一个缩进单位。因此空格或者tab的数量很灵活。google的代码,多使用两个空格进行缩进,这样复杂的,拥有多个缩进层次的代码,一行可以占用比较少的字符数量。

甚至还有人使用3个空格作为一个缩进单位。

关键字

python中关键字如下代码所示,需要注意,有些常用功能如len,dir,print等为内建函数,不是关键字。

import keyword

print(keyword.kwlist)
['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']

重定义关键字是非法行为,会导致错误。

and = 1
  File "<ipython-input-3-a3fd417c5b1e>", line 1
    and = 1
      ^
SyntaxError: invalid syntax

基础数据类型及相应运算

作为一门通用编程语言,Python中也有整数,浮点数,布尔型,字符串这几中基础数据类型,并且这几种类型的操作与其他编程语言基本一致。

数字

x = 3
print((x, type(x)))
(3, <class 'int'>)
print(x + 1)  # 加法;
print(x - 1)  # 减法;
print(x * 2)  # 乘法;
print(x ** 2)  # 指数运算;
print(x / 2)  # divide除
print(x // 2)  # 地板除,向下取整
4
2
6
9
1.5
1

Python中也有复合运算符,但是没有++ --这种操作符。

x += 1
print(x)
x *= 2
print(x)
4
8
x++
x--
  File "<ipython-input-7-162eb8f3e7b9>", line 1
    x++
       ^
SyntaxError: invalid syntax

浮点数运算与整数基本相同,这里不再赘述。

y = 2.5
z = 1e-3  # python中的科学计数法
print(type(y), type(z))  # Prints "<type 'float'>"
print(y, y + 1, y * 2, y ** 2)  # Prints "2.5 3.5 5.0 6.25"
print(z)
<class 'float'> <class 'float'>
2.5 3.5 5.0 6.25
0.001

python也内建了复数运算的支持,关于python中算数运算的更详细内容,请参考这个文档https://docs.python/3/library/stdtypes.html#numeric-types-int-float-long-complex

和其他语言一样,python中的浮点数也有精度限制,对比两个浮点数的时候,不能使用==号比较。

python数字是没有值域限制的,可以表示任意大的数字。

"""
x = 2  # 著名的国际象棋的故事
for i in range(64):
    x = x*2
print(x) # 这个数字太大了,打印就要几十分钟
"""

x = 2  # 稍微改一下
for i in range(16):
    x = x ** 2
print(x)
2003529930406846464979072351560255750447825475569751419265016973710894059556311453089506130880933348101038234342907263181822949382118812668869506364761547029165041871916351587966347219442930927982084309104855990570159318959639524863372367203002916969592156108764948889254090805911457037675208500206671563702366126359747144807111774815880914135742720967190151836282560618091458852699826141425030123391108273603843767876449043205960379124490905707560314035076162562476031863793126484703743782954975613770981604614413308692118102485959152380195331030292162800160568670105651646750568038741529463842244845292537361442533614373729088303794601274724958414864915930647252015155693922628180691650796381064132275307267143998158508811292628901134237782705567421080070065283963322155077831214288551675554073345107213112427399562982719769150054883905223804357045848197956393157853510018992000024141963706813559840464039472194016069517690156119726982337890017641517190051133466306898140219383481435426387306539552969691388024158161859561100640362119796101859534802787167200122604642492385111393400464351623867567078745259464670903886547743483217897012764455529409092021959585751622973333576159552394885297579954028471943529913543763705986928913757153740001986394332464890052543106629669165243419174691389632476560289415199775477703138064781342309596190960654591300890188887588084733625956065444888501447335706058817090162108499714529568344061979690565469813631162053579369791403236328496233046421066136200220175787851857409162050489711781820400187282939943446186224328009837323764931814789848119452713007440220765680910376203999203492023906626264491909167985461515778839060397720759279378852241294301017458086862263369284725851403039615558564330385450688652213114813638408384778263790459607186876728509763471271988890680478243230394718650525660978150729861141430305816927924971409161059417185352275887504477592218301158780701975535722241400019548102005661773589781499532325208589753463547007786690406429016763808161740550405117670093673202804549339027992491867306539931640720492238474815280619166900933805732120816350707634351669869625020969023162859350071874190579161241536897514808261904847946571736601005892476655445840838334790544144817684255327207315586349347605137419779525190365032198020108764738368682531025183377533908861426184800374008082238104076468878471647552945326947661700424461063311238021134588694532200116564076327023074292426051582811070387018345324567635625951430032037432740780879056283663406965030844225855967039271869461158513793386475699748568670079823960604393478850861649260304945061743412365828352144806726676841807083754862211408236579802961200027441324438432402331257403545019352428776430880232850855886089962774458164680857875115807014743763867976955049991643998284357290415378143438847303484261903388841494031366139854257635577105335580206622185577060082551288893332226436281984838613239570676191409638533832374343758830859233722284644287996245605476932428998432652677378373173288063210753211238680604674708428051166488709084770291208161104912555598322366244868556651402684641209694982590565519216188104341226838996283071654868525536914850299539675503954938371853405900096187489473992880432496373165753803673586710175783994818471798498246948060532081996066183434012476096639519778021441199752546704080608499344178256285092726523709898651539462193004607364507926212975917698293892367015170992091531567814439791248475706237804600009918293321306880570046591458387208088016887445835557926258465124763087148566313528934166117490617526671492672176128330845273936469244582892571388877839056300482483799839692029222215486145902373478222682521639957440801727144146179559226175083889020074169926238300282286249284182671243405751424188569994272331606998712986882771820617214453142574944015066139463169197629181506579745526236191224848063890033669074365989226349564114665503062965960199720636202603521917776740668777463549375318899587866282125469797102065747232721372918144666659421872003474508942830911535189271114287108376159222380276605327823351661555149369375778466670145717971901227117812780450240026384758788339396817962950690798817121690686929538248529830023476068454114178139110648560236549754227497231007615131870024053910510913817843721791422528587432098524957878034683703337818421444017138688124249984418618129271198533315382567321870421530631197748535214670955334626336610864667332292409879849256691109516143618601548909740241913509623043612196128165950518666022030715613684732364660868905014263913906515063908199378852318365059897299125404479443425166774299659811849233151555272883274028352688442408752811283289980625912673699546247341543333500147231430612750390307397135252069338173843322950701049061867539433130784798015655130384758155685236218010419650255596181934986315913233036096461905990236112681196023441843363334594927631946101716652913823717182394299216272538461776065694542297877071383198817036964588689811863210976900355735884624464835706291453052757101278872027965364479724025405448132748391794128826423835171949197209797145936887537198729130831738033911016128547415377377715951728084111627597186384924222802373441925469991983672192131287035585307966942713416391033882754318613643490100943197409047331014476299861725424423355612237435715825933382804986243892498222780715951762757847109475119033482241412025182688713728193104253478196128440176479531505057110722974314569915223451643121848657575786528197564843508958384722923534559464521215831657751471298708225909292655638836651120681943836904116252668710044560243704200663709001941185557160472044643696932850060046928140507119069261393993902735534545567470314903886022024639948260501762431969305640666366626090207048887438898907498152865444381862917382901051820869936382661868303915273264581286782806601337500096593364625146091723180312930347877421234679118454791311109897794648216922505629399956793483801699157439700537542134485874586856047286751065423341893839099110586465595113646061055156838541217459801807133163612573079611168343863767667307354583494789788316330129240800836356825939157113130978030516441716682518346573675934198084958947940983292500086389778563494693212473426103062713745077286156922596628573857905533240641849018451328284632709269753830867308409142247659474439973348130810986399417379789657010687026734161967196591599588537834822988270125605842365589539690306474965584147981310997157542043256395776070485100881578291408250777738559790129129407309462785944505859412273194812753225152324801503466519048228961406646890305102510916237770448486230229488966711380555607956620732449373374027836767300203011615227008921843515652121379215748206859356920790214502277133099987729459596952817044582181956080965811702798062669891205061560742325686842271306295009864421853470810407128917646906550836129916694778023822502789667843489199409657361704586786242554006942516693979292624714524945408858422726153755260071904336329196375777502176005195800693847635789586878489536872122898557806826518192703632099480155874455575175312736471421295536494084385586615208012115079075068553344489258693283859653013272046970694571546959353658571788894862333292465202735853188533370948455403336565356988172582528918056635488363743793348411845580168331827676834646291995605513470039147876808640322629616641560667508153710646723108461964247537490553744805318226002710216400980584497526023035640038083472053149941172965736785066421400842696497103241919182121213206939769143923368374709228267738708132236680086924703491586840991153098315412063566123187504305467536983230827966457417620806593177265685841681837966106144963432544111706941700222657817358351259821080769101961052229263879745049019254311900620561906577452416191913187533984049343976823310298465893318373015809592522829206820862230332585280119266496314441316442773003237792274712330696417149945532261035475145631290668854345426869788447742981777493710117614651624183616680254815296335308490849943006763654806102940094693750609845588558043970485914449584445079978497045583550685408745163316464118083123079704389849190506587586425810738422420591191941674182490452700288263983057950057341711487031187142834184499153456702915280104485145176055306971441761368582384102787659324662689978418319620312262421177391477208004883578333569204533935953254564897028558589735505751235129536540502842081022785248776603574246366673148680279486052445782673626230852978265057114624846595914210278122788941448163994973881884622768244851622051817076722169863265701654316919742651230041757329904473537672536845792754365412826553581858046840069367718605020070547247548400805530424951854495267247261347318174742180078574693465447136036975884118029408039616746946288540679172138601225419503819704538417268006398820656328792839582708510919958839448297775647152026132871089526163417707151642899487953564854553553148754978134009964854498635824847690590033116961303766127923464323129706628411307427046202032013368350385425360313636763575212604707425311209233402837482949453104727418969287275572027615272268283376741393425652653283068469997597097750005560889932685025049212884068274139881631540456490350775871680074055685724021758685439053228133770707415830756269628316955687424060527726485853050611356384851965918968649596335568216975437621430778665934730450164822432964891270709898076676625671517269062058815549666382573829274182082278960684488222983394816670984039024283514306813767253460126007269262969468672750794346190439996618979611928750519442356402644303271737341591281496056168353988188569484045342311424613559925272330064881627466723523751234311893442118885085079358163848994487544756331689213869675574302737953785262542329024881047181939037220666894702204258836895840939998453560948869946833852579675161882159410981624918741813364726965123980677561947912557957446471427868624053750576104204267149366084980238274680575982591331006919941904651906531171908926077949119217946407355129633864523035673345588033313197080365457184791550432654899559705862888286866606618021882248602144999973122164138170653480175510438406624412822803616648904257377640956326482825258407669045608439490325290526337532316509087681336614242398309530806549661879381949120033919489494065132398816642080088395554942237096734840072642705701165089075196155370186264797456381187856175457113400473810762763014953309735174180655479112660938034311378532532883533352024934365979129341284854970946826329075830193072665337782559314331110963848053940859283988907796210479847919686876539987477095912788727475874439806779824968278272200926449944559380414608770641941810440758269805688038949654616587983904660587645341810289907194293021774519976104495043196841503455514044820928933378657363052830619990077748726922998608279053171691876578860908941817057993404890218441559791092676862796597583952483926734883634745651687016166240642424241228961118010615682342539392180052483454723779219911228595914191877491793823340010078128326506710281781396029120914720100947878752551263372884222353869490067927664511634758101193875319657242121476038284774774571704578610417385747911301908583877890152334343013005282797038580359815182929600305682612091950943737325454171056383887047528950563961029843641360935641632589408137981511693338619797339821670761004607980096016024823096943043806956620123213650140549586250615282588033022908385812478469315720323233601899469437647726721879376826431828382603564520699468630216048874528424363593558622333506235945002890558581611275341783750455936126130852640828051213873177490200249552738734585956405160830583053770732533971552620444705429573538361113677523169972740292941674204423248113875075631319078272188864053374694213842169928862940479635305150560788126366206497231257579019598873041195626227343728900516561111094111745277965482790471250581999077498063821559376885546498822938985408291325129076478386322494781016753491693489288104203015610283386143827378160946341335383578340765314321417150655877547820252454780657301342277470616744241968952613164274104695474621483756288299771804186785084546965619150908695874251184435837306590951460980451247409411373899927822492983367796011015387096129749705566301637307202750734759922943792393824427421186158236161317886392553095117188421298508307238259729144142251579403883011359083331651858234967221259621812507058113759495525022747274674369887131926670769299199084467161228738858457584622726573330753735572823951616964175198675012681745429323738294143824814377139861906716657572945807804820559511881687188075212971832636442155336787751274766940790117057509819575084563565217389544179875074523854455200133572033332379895074393905312918212255259833790909463630202185353848854825062897715616963860712382771725621313460549401770413581731931763370136332252819127547191443450920711848838366818174263342949611870091503049165339464763717766439120798347494627397822171502090670190302469762151278521956142070806461631373236517853976292092025500288962012970141379640038055734949269073535145961208674796547733692958773628635660143767964038430796864138563447801328261284589184898528048048844180821639423974014362903481665458114454366460032490618763039502356402044530748210241366895196644221339200757479128683805175150634662569391937740283512075666260829890491877287833852178522792045771846965855278790447562192663992008409302075673925363735628390829817577902153202106409617373283598494066652141198183810884515459772895164572131897797907491941013148368544639616904607030107596818933741217575988165127000761262789169510406315857637534787420070222051070891257612361658026806815858499852631465878086616800733264676830206391697203064894405628195406190685242003053463156621891327309069687353181641094514288036605995220248248886711554429104721929134248346438705368508648749099178812670565665387191049721820042371492740164460943459845392536706132210616533085662021188968234005752675486101476993688738209584552211571923479686888160853631615862880150395949418529489227074410828207169303387818084936204018255222271010985653444817207470756019245915599431072949578197878590578940052540122867517142511184356437184053563024181225473266093302710397968091064939272722683035410467632591355279683837705019855234621222858410557119921731717969804339317707750755627056047831779844447637560254637033369247114220815519973691371975163241302748712199863404548248524570118553342675264715978310731245663429805221455494156252724028915333354349341217862037007260315279870771872491234494477147909520734761385425485311552773301030342476835865496093722324007154518129732692081058424090557725645803681462234493189708138897143299831347617799679712453782310703739151473878692119187566700319321281896803322696594459286210607438827416919465162267632540665070881071030394178860564893769816734159025925194611823642945652669372203155504700213598846292758012527715422016629954863130324912311029627923723899766416803497141226527931907636326136814145516376656559839788489381733082668779901962886932296597379951931621187215455287394170243669885593888793316744533363119541518404088283815193421234122820030950313341050704760159987985472529190665222479319715440331794836837373220821885773341623856441380700541913530245943913502554531886454796252260251762928374330465102361057583514550739443339610216229675461415781127197001738611494279501411253280621254775810512972088465263158094806633687670147310733540717710876615935856814098212967730759197382973441445256688770855324570888958320993823432102718224114763732791357568615421252849657903335093152776925505845644010552192644505312073756287744998163646332835816140330175813967359427327690448920361880386754955751806890058532927201493923500525845146706982628548257883267398735220457228239290207144822219885587102896991935873074277815159757620764023951243860202032596596250212578349957710085626386118233813318509014686577064010676278617583772772895892746039403930337271873850536912957126715066896688493880885142943609962012966759079225082275313812849851526902931700263136328942095797577959327635531162066753488651317323872438748063513314512644889967589828812925480076425186586490241111127301357197181381602583178506932244007998656635371544088454866393181708395735780799059730839094881804060935959190907473960904410150516321749681412100765719177483767355751000733616922386537429079457803200042337452807566153042929014495780629634138383551783599764708851349004856973697965238695845994595592090709058956891451141412684505462117945026611750166928260250950770778211950432617383223562437601776799362796099368975191394965033358507155418436456852616674243688920371037495328425927131610537834980740739158633817967658425258036737206469351248652238481341663808061505704829059890696451936440018597120425723007316410009916987524260377362177763430621616744884930810929901009517974541564251204822086714586849255132444266777127863728211331536224301091824391243380214046242223349153559516890816288487989988273630445372432174280215755777967021666317047969728172483392841015642274507271779269399929740308072770395013581545142494049026536105825409373114653104943382484379718606937214444600826798002471229489405761853892203425608302697052876621377373594394224114707074072902725461307358541745691419446487624357682397065703184168467540733466346293673983620004041400714054277632480132742202685393698869787607009590048684650626771363070979821006557285101306601010780633743344773073478653881742681230743766066643312775356466578603715192922768440458273283243808212841218776132042460464900801054731426749260826922155637405486241717031027919996942645620955619816454547662045022411449404749349832206807191352767986747813458203859570413466177937228534940031631599544093684089572533438702986717829770373332806801764639502090023941931499115009105276821119510999063166150311585582835582607179410052528583611369961303442790173811787412061288182062023263849861515656451230047792967563618345768105043341769543067538041113928553792529241347339481050532025708728186307291158911335942014761872664291564036371927602306283840650425441742335464549987055318726887926424102147363698625463747159744354943443899730051742525110877357886390946812096673428152585919924857640488055071329814299359911463239919113959926752576359007446572810191805841807342227734721397723218231771716916400108826112549093361186780575722391018186168549108500885272274374212086524852372456248697662245384819298671129452945515497030585919307198497105414181636968976131126744027009648667545934567059936995464500558921628047976365686133316563907395703272034389175415267500915011198856872708848195531676931681272892143031376818016445477367518353497857924276463354162433601125960252109501612264110346083465648235597934274056868849224458745493776752120324703803035491157544831295275891939893680876327685438769557694881422844311998595700727521393176837831770339130423060958999137314684569010422095161967070506420256733873446115655276175992727151877660010238944760539789516945708802728736225121076224091810066700883474737605156285533943565843756271241244457651663064085939507947550920463932245202535463634444791755661725962187199279186575490857852950012840229035061514937310107009446151011613712423761426722541732055959202782129325725947146417224977321316381845326555279604270541871496236585252458648933254145062642337885651464670604298564781968461593663288954299780722542264790400616019751975007460545150060291806638271497016110987951336633771378434416194053121445291855180136575558667615019373029691932076120009255065081583275508499340768797252369987023567931026804136745718956641431852679054717169962990363015545645090044802789055701968328313630718997699153166679208958768572290600915472919636381673596673959975710326015571920237348580521128117458610065152598883843114511894880552129145775699146577530041384717124577965048175856395072895337539755822087777506072339445587895905719156736

python3.6中为了数字的显示方便,增加了数字的千分位符,如下,可以用横线来分割数字。

# 千分位符
a = 368934881474191032321
b = 368_934_881_474_191_032_321
c = 123.456_789
print(a == b)
type(c)
True





float

比较运算符

a = 1
b = 2.5
c = 1e5

print(a > b, a < b, a == b)
print(a >= b, a <= b, a != b)
False True False
False True True

== 和 != 两个运算符还可以用来做对象之间的比较。

另外,后续介绍,如果某个对象实现了提供比较功能的 magic method,则这个对象也支持大小比较。

十进制、八进制、十六进制、二进制与位运算

计算机中,用磁化的有无状态表示数据10,因此,计算机内部的数据表示全部都是二进制的。

也正是因此,与日常生活中常用的10进制不同,计算机科学中常用的是 2/8/16进制来表示数字。

python内建了对这几种进制的支持和相关转换。

a = 123
print(bin(a))  # 转换为二进制
print(oct(a))  # 转换为二进制
print(hex(a))  # 转换为二进制
print(type(a))  # 转换为二进制
0b1111011
0o173
0x7b
<class 'int'>

需要注意,上述几个操作,其最后输出的数据都是字符串格式的,只是为了显示方便。可以通过int操作转换回数字。

print(int(bin(a), base=2))  # 当字符串不是10进制的时候,需要手动指定使用的进制
print(int(oct(a), base=8))
print(int(hex(a), base=16))
123
123
123

也可以在定义变量的时候直接使用不同的进制定义。

a = 0b1111011
b = 0o173
c = 0x7B

print(a, b, c)
123 123 123

python支持整型数据的位运算

a = 311
b = 456
print(bin(a))
print(bin(b))

print(bin(a & b))  # 与
print(bin(a | b))  # 或
print(bin(a ^ b))  # 非
print(bin(~b))  # 异或

print(bin(a << 1))  # 左移,乘以2
print(bin(a >> 1))  # 右移,除以2
0b100110111
0b111001000
0b100000000
0b111111111
0b11111111
-0b111001001
0b1001101110
0b10011011

位操作在硬件编程中比较常用,而且效率也比较高,普通场景很少用到位操作。

布尔型

t, f = True, False
print(type(t))
<class 'bool'>

需要注意的是,有C语言背景的话,C语言里面True和False是1和0的宏定义,但是Python里面不是这样的,Python里面True和False是保留字,是一个单独的定义,不能与1,0混用,同时还有一个None类型,与C语言中的NULL类似,但是也不是0的宏定义。

另外,python中逻辑运算全部用字母运算符表示,而且是保留关键字,这样能够避免语法的一些混乱。

print(t and f)  # 逻辑与;
print(t or f)  # 逻辑或;
print(not t)  # 逻辑非;
print(None == 1)
print(None == False)
print(type(None))
False
True
False
False
False
<class 'NoneType'>

字符串

在C或者java中使用字符串的时候,字符串中间插入单双引号需要进行转义,而python中为了解决这类问题,直接将单双引号等价,都可以用来定义字符串,而且是等价的,所以可以在一个字符串里面根据需要混用两种引号。对于多行字符串,还可以使用三单引号来定义。

hello = 'hel"lo!'
world = "w'r'ld"
hello_world = """
'hello'
world
"""
print(hello, len(hello))
print(hello_world)
hel"lo! 7

'hello'
world
hw = hello + " " + world
print(hw)
x = 2
y = 2.1
print(hello + str(x) + str(y))  # 数字转换成字符串
print(int(str(x)), float(str(y)))  # 同样的,字符串也可以转换成数字
hel"lo! w'r'ld
hel"lo!22.1
2 2.1
hw12 = "%s %s %d %.2f" % (hello, world, 12, 123.4567)  # 老式C风格字符串格式化
print(hw12)  # prints "hello world 12"

hw13 = "{0} {1} {2} {3:.2f}".format(hello, world, 12, 123.4567)  # python风格字符串格式化
print(hw13)

hw14 = "{} {} {} {:.2f}".format(hello, world, 12, 123.4567)  # python风格字符串格式化

print(hw14)
x = 123.4567
hwf = f"{hello} {world} {x:.2f}"  # python3.6以后才支持的f格式化
print(hwf)
hel"lo! w'r'ld 12 123.46
hel"lo! w'r'ld 12 123.46
hel"lo! w'r'ld 12 123.46
hel"lo! w'r'ld 123.46

字符串内建了一系列有用的方法:

s = "hello"
print(s.capitalize())  # 首字母大写
print(s.upper())  # 大写
print(s.rjust(7))  # 右边空格补齐
print(s.center(7))  # 剧中对齐
print(s.replace("l", "(ell)"))  # 搜索替换
print("  world ".strip())  # 删除空白字符
Hello
HELLO
  hello
 hello 
he(ell)(ell)o
world

更详细的字符串相关文档,可以查阅https://docs.python/3/library/stdtypes.html#string-methods

字符串也可以作为一个序列对象被遍历 ,后续会介绍到。

for char in hello_world:  # 字符串也可以被for in,虽然这种用法的场景比较少
    print(char)
'
h
e
l
l
o
'


w
o
r
l
d

bytes

bytes一般是跟字符串一起使用的,同时作为计算机中对数据的原始表示,bytes也有自己独特的用途.

b = b"this is a bytes"  # bytes的定义,在python3中是以b开头的.
print(b.decode())  # bytes 经过decode可以转化成string
print("this is a string".encode())  # 同样的,string也可以通过encode转化成bytes

print(b.decode(encoding="ascii"))  # ascii是默认的转换编码方式
print("this is a string".encode(encoding="ascii"))  #

print(b.decode(encoding="utf-8"))  # 在编码兼容的情况下,也可以换其他编码处理
print("this is a string".encode(encoding="utf-8"))  #

print("这是个字符串".encode(encoding="utf-8"))
print("这是个字符串".encode(encoding="gb2312"))  # 同一个字符串,用不同的编码方式得到的bytes是不一样的
print("这是个字符串".encode(encoding="ascii"))
# 有的字符串字符不再某些编码体系中,也就无法使用对应的编码
# 一般出现UnicodeEncodeError,是选错了编码导致的
this is a bytes
b'this is a string'
this is a bytes
b'this is a string'
this is a bytes
b'this is a string'
b'\xe8\xbf\x99\xe6\x98\xaf\xe4\xb8\xaa\xe5\xad\x97\xe7\xac\xa6\xe4\xb8\xb2'
b'\xd5\xe2\xca\xc7\xb8\xf6\xd7\xd6\xb7\xfb\xb4\xae'



---------------------------------------------------------------------------

UnicodeEncodeError                        Traceback (most recent call last)

<ipython-input-23-db70c82107ae> in <module>
     11 print("这是个字符串".encode(encoding="utf-8"))
     12 print("这是个字符串".encode(encoding="gb2312"))  # 同一个字符串,用不同的编码方式得到的bytes是不一样的
---> 13 print("这是个字符串".encode(encoding="ascii"))
     14 # 有的字符串字符不再某些编码体系中,也就无法使用对应的编码
     15 # 一般出现UnicodeEncodeError,是选错了编码导致的


UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

注释

python可以使用不同风格的注释来对代码进行解释

x = 1  # 这是一个单行注释
# 这也是一个单行注释
"任何一个没有变量承接的字符串都可以作为一个注释"

"""
python中没有单独的多行注释语法,
多行注释通过多行字符串来实现
"""
print("hello")

"""
x = 1
y =2 
对于需要临时注释掉的代码,可以使用多行注释来处理
因为字符串的左右边界字符是完全一样的,
所以一定注意不管使用单引号双引号还是三引号,都一定要成对使用
"""
hello





'\nx = 1\ny =2 \n对于需要临时注释掉的代码,可以使用多行注释来处理\n因为字符串的左右边界字符是完全一样的,\n所以一定注意不管使用单引号双引号还是三引号,都一定要成对使用\n'

需要注意的是,python中还有一些高级的注释用法,有些注释可以被一些工具自动识别并处理,后续会介绍到。

变量

python为动态强类型语言,变量可以声明即定义,或者都在某些特定场合先声明后定义。

a = 1
print(a, type(a))  # 可以使用type内建函数,获得变量的类型。
1 <class 'int'>
b = 2


def fun():
    """
    与其他语言一样,函数内部定义的变量会隐藏外部同名变量,
    这里函数内外的b是两个完全独立的变量

    这里,函数名后面跟的多行字符串,是一种标准的注释格式,
    这个多行字符串的内容会被一些工具提取作为这个函数的文档说明。
    """
    b = 3

    def fun_inner():
        """
        这是一个内嵌函数,一般来说这种使用方式应该避免,但是在使用装饰器的时候,
        比较常用这种定义,
        """
        b = 4
        print(b)

    fun_inner()
    print(b)


fun()
print(b)
4
3
2
b = 2


def fun():
    """
    使用global可以让函数内部能够够访问外部变量定义而不会引起覆盖
    """
    global b
    b = 3
    print(b)


fun()
print(b)
3
3
b = 2


def fun():
    b = 3

    def fun_inner():
        """
        在内部函数中可以使用nonlocal来访问外部函数的变量,这种技术是在编程中称为闭包的一种技术的基础。
        """
        nonlocal b
        b = 4
        print(b)

    fun_inner()
    print(b)


fun()
print(b)
4
4
2

下面是python中,关于变量的使用,有点坑的地方

for i in range(10):
    inner_var2 = i + 1
    print(inner_var2)
print(i, inner_var2)

a = 1
if a > 0:
    inner_var = 2  # 这一句因为判断条件满足而被执行了。
else:
    inner_var2 = 3
print(inner_var)
# 这里,inner_var是在if语句中定义的,但是在if语句外面,这个变量仍然可以访问。
# 同样的情况发生在for,while循环等语句中
print(inner_var2)
# 这里的inner_var2用的是上面的for循环中定义的inner_var2,而不是if语句中的,
# 如果没有上面的for循环,这里应该是要报错的
# 这是一种比较常见的问题,循环结束后,没有确认就重用了循环里面的变量
1
2
3
4
5
6
7
8
9
10
9 10
2
10

在python中,各语句不形成变量作用域,只有函数和class能够形成变量作用域

控制结构

循环

python拥有比较独特的循环结构,特别方便在程序中使用。

while循环
n = 100
count = 0
while n > 0:
    count += n
    n -= 1
else:
    print("end while loop")
    # 当循环条件不满足的时候,这一行会被执行,平时比较少用,只在一些特殊的场合用到
    # 如果没有这个else,使用if也可以进行判断,但要多写几行代码

print("sum from 1 to %d is: %d" % (100, count))
end while loop
sum from 1 to 100 is: 5050
range函数

在介绍for循环之前,先介绍一下range函数,这个函数可以在需要使用数字序列的时候,产生一个我们需要的数字序列。
一定注意range的右边界不可达。

print(list(range(5)))  # 默认从0开始
print(list(range(0, 10,)))  # 也可以手动指定起始
print(list(range(0, 10, 2)))  # 默认间隔为1,可以手动指定
print(list(range(10, 0, -1)))  # 甚至可以逆序
print(range(10, 0, -1))  # range生成的是一个generator,可以用于循环,
# 但直接打印是无法打印出里面的数字的,必须包一个list
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 2, 4, 6, 8]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
range(10, 0, -1)
for循环

有了range,我们可以改写一下上面那个while循环了

count = 0
for i in range(101):  # range右边界不可达
    count += i
else:
    print("end for loop")  # for也有类似while的else结构
print("sum from 1 to %d is: %d" % (100, count))
end for loop
sum from 1 to 100 is: 5050

事实上 ,后续介绍的所有容器类型都可以使用for循环来遍历,详情参考后续内容。

sum(range(101))  # 上面的代码都是演示,对于这个求和,有更简单直观的代码解决
5050
break/continue

循环中,并不一定需要针对所有的循环条件运行循环体,特别是有些特殊的行情况需要提前结束循环执行。

count = 0
for i in range(101):  # range右边界不可达
    if i % 2 == 0:
        continue
    count += i
    if i > 50:
        break
else:
    print("end for loop")  # break的时候,并不是由于循环条件不满足,所以这一句没有执行
print("sum of odd number from 1 to %d is: %d" % (50, count))
sum of odd number from 1 to 50 is: 676

分支

python中的分支只有if,没有C/java等的switch结构

事实上,python里面也不需要switch结构,结合一些容器,遍历等语法,python的代码控制可以更加强大而简洁。

from math import sqrt

for i in range(10):
    if i % 2 == 0:
        print("{} is a even number".format(i))  # 偶数
    elif i % 2 != 0:  # 连续分支,可以替代switch结构
        for j in range(2, int(sqrt(i) + 1)):  # 只针对奇数判断素数,偶数肯定是合数
            if i % j == 0:
                print("{} is a composite number".format(i))
                # 能被1和自己之外的数字整除,合数,后续计算没必要进行了
                break
                # else可以没有
        else:  # 注意这里是for循环的else
            print("{} is a prime number".format(i))  # 只能被1和自己整除,所以是素数
        print("{} is a odd number".format(i))  # 奇数
    else:
        print("this not even possible for {}".format(i))
0 is a even number
1 is a prime number
1 is a odd number
2 is a even number
3 is a prime number
3 is a odd number
4 is a even number
5 is a prime number
5 is a odd number
6 is a even number
7 is a prime number
7 is a odd number
8 is a even number
9 is a composite number
9 is a odd number

注意,我们这里的代码有个小漏洞,2是偶数,但同时2也是素数。

关于三目预算符,python里面是没有的,但是我们可以这么写:

flag = True
x = 1 if flag else 2

if flag:
    x = 1
else:
    x = 2

函数

python里面,函数是由def来定义的:

def sign(x):
    if x > 0:
        return "positive"
    elif x < 0:
        return "negative"
    else:
        return "zero"


print(sign(-1))
print(sign(0))
print(sign(1))
negative
zero
positive

python的函数还可以接受带默认值的keyword参数

def hello(name, loud=False):
    if loud:
        print("HELLO, %s" % name.upper())
    else:
        print("Hello, %s!" % name)


hello("Bob")
hello("Fred", loud=True)
hello("Fred", True)  # 不写keyword的情况下,也可以按照顺序判断传入的是哪个参数
hello(loud=True, name="Ray")
# python里面的参数,只要指定了name,
# 就可以识别,不需要按照定义的顺序给出
Hello, Bob!
HELLO, FRED
HELLO, FRED
HELLO, RAY

函数的参数

Python函数还能够接受变长参数,也就是定义的时候,可以不规定参数,在运行的时候实时解析参数。

def fun(var1, var2, var3=3, var4=4, **kw):
    """
    kw即为可变参数,任何传入了,但不属于前面定义的已知参数都会被收集到这个kw里面供处理
    这个kw为python里面字典类型,关于字典类型参考后面的介绍
    
    还记得么,这个多行字符串是标准python注释的一种格式
    """

    print(kw)
    print(var1, var2, var3, var4)


fun(2, 1, var4=5, var5=5, var6=6)
{'var5': 5, 'var6': 6}
2 1 3 5
def fun2(var1, var2=3, *var3):
    """
    对于不带keyword的顺序参数,可以使用星号语法进行收集
    """
    print(var3)
    print(var1, var2)


fun2(1, 2, 3, 4, 5)
fun2(1, 2, *[3, 4, 5])  # 星号语法能收集参数,也可以用来传递参数。
(3, 4, 5)
1 2
(3, 4, 5)
1 2
def fun3(var1, var2=3, *var3, **kw):
    """
    顺序参数,keyword参数,和变长参数可以结合起来使用
    """
    print(kw)
    print(var3)
    print(var1, var2)


fun3(1, 2, *[3, 4, 5], **{"var4": 5, "var5": 6, "var6": 6})
# 星号语法也可以用来传递字典类型的参数
{'var4': 5, 'var5': 6, 'var6': 6}
(3, 4, 5)
1 2
def fun4(var1, *var3, var2=3, **kw):
    """
    需要注意这种情况,var2只能由keyword传入,不然就被var3收集起来了
    """
    print(kw)
    print(var3)
    print(var1, var2)


fun4(1, 2, *[3, 4, 5], **{"var4": 5, "var5": 6, "var6": 6})
fun4(1, 2, *[3, 4, 5], var2=100, **{"var4": 5, "var5": 6, "var6": 6})
{'var4': 5, 'var5': 6, 'var6': 6}
(2, 3, 4, 5)
1 3
{'var4': 5, 'var5': 6, 'var6': 6}
(2, 3, 4, 5)
1 100
def fun5(var1,  *var3, **kw, var2=3):
    '''
    当结合使用的时候,定义函数要遵守以下规则:
    1 星号顺序参数必须在星号字典参数的前面
    2 顺序参数必须在keyword参数的前面
    3 keyword参数必须在星号字典参数的前面
    '''
    print(kw)
    print(var3)
    print(var1, var2)


fun5(1, 2, *[3, 4, 5], **{'var4': 5, 'var5': 6, 'var6': 6})
  File "<ipython-input-43-8a10004febe0>", line 1
    def fun5(var1,  *var3, **kw, var2=3):
                                    ^
SyntaxError: invalid syntax

强烈建议全部使用keyword参数来定义和使用函数,这样能避免很多问题。

函数的使用

函数在python中也是一种类型,可以被当作参数传递,再拿偶数奇数,素数合数的例子,改写一下

from math import sqrt


def fun_odd(var):
    print("{} is a odd number".format(var))


def fun_even(var):
    print("{} is a even number".format(var))


def fun_primary(var):
    print("{} is a prime number".format(var))


def fun_composite(var):
    print("{} is a composite number".format(var))


def is_primary(var):
    ret = False
    for j in range(2, int(sqrt(var) + 1)):  # 只针对奇数判断素数,偶数肯定是合数
        if var % j == 0:
            break
    else:  # 注意这里是for循环的else
        ret = True
    return ret


def judge_numbers(
    number,
    odd_fun=fun_odd,
    even_fun=fun_even,
    primary_fun=fun_primary,
    composite_fun=fun_composite,
):
    for i in range(number):
        if i % 2 == 0:
            even_fun(i)
        elif i % 2 != 0:
            if is_primary(i):
                primary_fun(i)
            else:
                composite_fun(i)
            odd_fun(i)
        else:
            print("this not even possible for {}".format(i))


judge_numbers(10)
print(is_primary, type(is_primary))
0 is a even number
1 is a prime number
1 is a odd number
2 is a even number
3 is a prime number
3 is a odd number
4 is a even number
5 is a prime number
5 is a odd number
6 is a even number
7 is a prime number
7 is a odd number
8 is a even number
9 is a composite number
9 is a odd number
<function is_primary at 0x7f84d7ae33b0> <class 'function'>

关于函数的返回值,需要注意的是,任何python函数都是有返回值的,如果不写,则默认返回值为None,特别是有些内建函数,如果不注意会造成很隐蔽的bug。

a = fun_even(10)
print(a)
10 is a even number
None

lambda函数

有时,一些函数很简单,单独定义个函数有些没必要,但是一些接口又必须以函数作为参数,则可以使用lambda函数,或称匿名函数。

lambda函数只能有一条语句,而且不能 包含赋值语句,语句的返回值,就是lambda函数的返回值

from math import sqrt

# lambda函数也可以用一个变量来承接。
fun_composite = lambda var: print("{} is a composite number".format(var))


def is_primary(var):
    ret = False
    for j in range(2, int(sqrt(var) + 1)):  # 只针对奇数判断素数,偶数肯定是合数
        if var % j == 0:
            break
    else:  # 注意这里是for循环的else
        ret = True
    return ret


def judge_numbers(
    number,
    odd_fun=lambda var: print("{} is a odd number".format(var)),
    even_fun=lambda var: print("{} is a even number".format(var)),
    primary_fun=lambda var: print("{} is a prime number".format(var)),
    composite_fun=fun_composite,
):
    for i in range(number):
        if i % 2 == 0:
            even_fun(i)
        elif i % 2 != 0:
            if is_primary(i):
                primary_fun(i)
            else:
                composite_fun(i)
            odd_fun(i)
        else:
            print("this not even possible for {}".format(i))


judge_numbers(10)
print(fun_composite, type(fun_composite), fun_composite(15))

# lambda 也可以有多个参数
add = lambda x, y: x + y


print(add(123, 456))
0 is a even number
1 is a prime number
1 is a odd number
2 is a even number
3 is a prime number
3 is a odd number
4 is a even number
5 is a prime number
5 is a odd number
6 is a even number
7 is a prime number
7 is a odd number
8 is a even number
9 is a composite number
9 is a odd number
15 is a composite number
<function <lambda> at 0x7f84d7af5170> <class 'function'> None
579

类型推断

Python 本身是一门动态强类型语言,一个变量,在运行的时候,可以随意改变改变量所承接的值的类型,这称为动态。而变量的值,不同类型之间需要通过显示或者隐式的转换才能相互运算,这是强类型的特性。

Java C++ 等是典型的静态强类型语言,变量声明即规定了变量的类型,运行中除非继承类型,不然不能直接赋值,就算是继承类型,也只能调用该变量声明类型的各版本重载,不能调用子类的特有方法。

js 则是典型的动态弱类型语言,变量直接可以直接相互运算。

Python 的动态强类型特性,注定其对类型是极其敏感的,所以对 IDE 以及日常开发来说,变量不声明类型就给静态代码检查带来了很大的麻烦。很多代码,直到运行的时候,才能知道是否正确编写。

从 Pthon3.5 开始, Python 提供了类型提示(Type Hint)语法。

def say_hi(name):
    return "Hi," + name


# Python 3.5 以后的可宣写法


def say_hi(name: str) -> str:
    return "Hi," + name

注意类型提示是可选写法,官方推荐在定义函数的时候加上类型提示,但并不强制。更多内容可以参考:
https://docs.python/3/library/typing.html

容器类型与相关操作,comprehension

Python有几种内建的容器类型lists, dictionaries, sets, and tuples,其附带的各种内建操作,结合循环等,可以极大的简化某些场景下的数据操作。

Lists,列表

list是python版本的数组,与C/Java中的数组不同,list中的数组可以灵活的增删,而且可以存储不同类型的数据。

xs = [3, 1, 2]
print(xs, xs[2])  # list的元素用下标访问,需要注意python中的下标从0开始
print(xs[-1])
len(xs)  # 内建函数len经常被用来计算容器的元素数量
[3, 1, 2] 2
2





3
xs[2] = "foo"  # list可以存储不同类型的元素,甚至是list
xs[0] = [1, 2, 3, 4]
print(xs)
[[1, 2, 3, 4], 1, 'foo']
xs.append("bar")  # 像堆栈一样使用list
print(xs)
x = xs.pop()  # 被删除的元素会被返回
print(x, xs)
[[1, 2, 3, 4], 1, 'foo', 'bar']
bar [[1, 2, 3, 4], 1, 'foo']

关于list的更多详细信息https://docs.python/3/tutorial/datastructures.html#more-on-lists

Slicing/切片

除了能够以下标访问某个单独的元素之外,python的容器还提供一种可以同时操作多个元素的方式,切片

nums = list(range(5))
print(nums)
print(nums[2:4])  # 获得从第2到第3个元素,左边界可达,右边界不可达
print(nums[4:2:-1])  # 逆序获取元素
print(nums[1:5:2])  # 隔一个元素获取一个元素,这里的2是步长
print(nums[5:1:-2])
print(nums[2:])  # 索引可以省略,代表获取到最后
print(nums[:2])
print(nums[:])  # 两个索引都省略,代表获取所有元素,相当于复制,但是这里有个坑,待会儿介绍
print(nums[:-1])  # 所以可以是负号,这个-1代表右边界,记得右边界不可达
nums[2:4] = [8, 9]  # 批量赋值
print(nums)
[0, 1, 2, 3, 4]
[2, 3]
[4, 3]
[1, 3]
[4, 2]
[2, 3, 4]
[0, 1]
[0, 1, 2, 3, 4]
[0, 1, 2, 3]
[0, 1, 8, 9, 4]
深层复制和浅层复制问题

这是一个小坑,deepcopy深层复制/shallowcopy浅层复制问题

这个问题存在与所有的编程语言中,但是每个语言中的表现不同,在python中,除了基础的数字类型之外,其他所有的类型都有这个问题。

这里考虑一个最简单的版本。

l = [[1, 2, 3], 4, 5, 6]  # 嵌套的list

l2 = l[:]  # 复制一个
print(l, l2)

l[2] = "a"
l2[2] = "b"
print(l, l2)  # 这里一切正常
[[1, 2, 3], 4, 5, 6] [[1, 2, 3], 4, 5, 6]
[[1, 2, 3], 4, 'a', 6] [[1, 2, 3], 4, 'b', 6]
l[0][2] = "str1"
l2[0][2] = "str2"
print(l, l2)  # 这里出问题了,对l2的修改影响了l
[[1, 2, 'str2'], 4, 'a', 6] [[1, 2, 'str2'], 4, 'b', 6]
# 事实上,list中存储的是对[1,2,3]这个内部list的引用,
# 复制的时候,只是复制了一个引用,并没有复制这个对象本身
print(id(l[0]) == id(l2[0]))  # id可以用来返回一个对象的地址,这里可以看到l和l2持有的是同一个对象。
# 这里就是一个很典型的shallowcopy浅层copy

# 当然,一些情况下,这个现象是需要的,但是也有一些情况需要避免这个现象
True
# 解决方案很简单,针对内部对象,单独进行复制。
l2 = l[:]
l2[0] = l[0][:]  # 这种操作就是deepcopy深层复制

l[0][2] = "str1"
l2[0][2] = "str2"
print(l, l2)
[[1, 2, 'str1'], 4, 'a', 6] [[1, 2, 'str2'], 4, 'a', 6]

这里介绍的是很简单很浅显的情况,但是python代码上了一定的规模,特别是引入面向对象之后,很多时候,对象的构建过程比较复杂,浅层copy就不太容易被发现,需要小心处理。

循环

python内建了强大的对容器类型的遍历支持,使用起来很方便。

animals = ["cat", "dog", "monkey"]
for animal in animals:
    print(animal)
cat
dog
monkey

有时候,只访问元素值是不够的,还需要知道 元素的索引可以使用enumerate 函数

animals = ["cat", "dog", "monkey"]
for idx, animal in enumerate(animals):
    print("#%d: %s" % (idx + 1, animal))
#1: cat
#2: dog
#3: monkey
List comprehensions/列表推导式

comprehensions是python内建的对容器元素进行操作的一种便捷方式,其非常简洁,当然很多时候不是必须的。

考虑需要把一个list中的整数都取平方这个应用场景,如果用for循环来做的话

nums = [0, 1, 2, 3, 4]
squares = []
for x in nums:
    squares.append(x ** 2)
print(squares)
[0, 1, 4, 9, 16]

而用comprehensions则可以表示成这样,代码简洁了很多

nums = [0, 1, 2, 3, 4]
squares = [x ** 2 for x in nums]
print(squares)
[0, 1, 4, 9, 16]

comprehensions 也可以包含判断条件

nums = [0, 1, 2, 3, 4]
even_squares = [x ** 2 for x in nums if x % 2 == 0]
print(even_squares)
[0, 4, 16]

Dictionaries字典

字典是键值对 (key, value)的一种数据结构,类似java中map,但是这里的字典可以存储任意类型的元素作为value,不过字典对key有一定的要求,这个稍后介绍。

d = {"cat": "cute", "dog": "furry"}
print(d["cat"])  # 字典也可以用下标获取
print("cat" in d)  # 检查某个元素是否在字典里
cute
True
d["fish"] = "wet"  # 设置某个元素的值
print(d["fish"])
wet
print(d["monkey"])  # 找没有的元素
---------------------------------------------------------------------------

KeyError                                  Traceback (most recent call last)

<ipython-input-63-a39172809540> in <module>
----> 1 print(d["monkey"])  # 找没有的元素


KeyError: 'monkey'
print(d.get("monkey", "N/A"))  # 可以给一个默认值,避免KeyError
print(d.get("fish", "N/A"))
N/A
wet
del d["fish"]  # 删除一个值
# 这个真的很少用,我自己完全没用过这个,比起删除
# 我更喜欢直接用comprehension加判断条件直接创建一个新的。
print(d.get("fish", "N/A"))  # "fish" is no longer a key; prints "N/A"
N/A

更多关于dict的资料可以查看python的官方文档https://docs.python/3/library/stdtypes.html#dict

dict的循环

dict也支持内建的forin遍历语法

d = {"person": 2, "cat": 4, "spider": 8}
for animal in d:
    legs = d[animal]
    print("A %s has %d legs" % (animal, legs))
A person has 2 legs
A cat has 4 legs
A spider has 8 legs

也可以直接访问键值对

d = {"person": 2, "cat": 4, "spider": 8}
for animal, legs in d.items():
    print("A %s has %d legs" % (animal, legs))
A person has 2 legs
A cat has 4 legs
A spider has 8 legs
Dictionary comprehensions

跟list的类似

nums = [0, 1, 2, 3, 4]
even_num_to_square = {x: x ** 2 for x in nums if x % 2 == 0}
print(even_num_to_square)
{0: 0, 2: 4, 4: 16}

如果有有其他语言特别是javascript的背景的人,可能已经注意到,python里面的dict跟json格式如出一辙,事实上python里面的dict,可以非常方便的用json这个库跟json字符串互相转化。

d = {"person": 2, "cat": 4, "spider": 8}
import json

var_json = json.dumps(d)
print(type(var_json))
print(var_json)
d2 = json.loads(var_json)
print(type(d2))
print(d2)
<class 'str'>
{"person": 2, "cat": 4, "spider": 8}
<class 'dict'>
{'person': 2, 'cat': 4, 'spider': 8}

Sets/集合

集合是唯一元素的list,跟list相似但又有独特的应用领域

animals = {"cat", "dog"}
print("cat" in animals)
print("fish" in animals)
True
False
animals.add("fish")
print("fish" in animals)
print(animals)
print(len(animals))
True
{'fish', 'dog', 'cat'}
3
animals.add("cat")  # 添加重复的元素不起作用
print(len(animals))
print(animals)
animals.remove("cat")
print(len(animals))
3
{'fish', 'dog', 'cat'}
2

元素值唯一的特性,让set有独特的应用领域,但是set的使用真的不如list和dict广泛,甚至也比不上tuple。

set相关的详细信息可以查阅https://docs.python/3/library/stdtypes.html#set

Loops: set使用内建的hash算法来处理元素的内容比对,而不是按照元素放入的顺序,这个跟list不太一样,set是无序(unordered)的。

无序仅仅是相对于放入顺序来说,其实set可以保证只要内容不变,每次输出的顺序是完全一样的。

animals = {"cat", "dog", "fish"}
for animal in animals:
    print(animal)

animals = {"cat", "dog", "fish"}
for idx, animal in enumerate(animals):
    print(("#%d: %s" % (idx + 1, animal)))
fish
dog
cat
#1: fish
#2: dog
#3: cat
Set comprehensions:

没啥好说得了,自己感受下

from math import sqrt

print({int(sqrt(x)) for x in range(30)})
{0, 1, 2, 3, 4, 5}

Tuples/元组

元组是不可变 (immutable)版本的list,其使用跟list基本一致,不同的是list的元素可变,而tuple的元素不可变,同时tuple可以作为dict的key,而list不行。

d = {(x, x + 1): x for x in range(10)}  # Create a dictionary with tuple keys
print(d)
t = (5, 6)  # Create a tuple
print(type(t))
print(t[1])
print(d[t])
print(d[(1, 2)])
{(0, 1): 0, (1, 2): 1, (2, 3): 2, (3, 4): 3, (4, 5): 4, (5, 6): 5, (6, 7): 6, (7, 8): 7, (8, 9): 8, (9, 10): 9}
<class 'tuple'>
6
5
1
t[0] = 1
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-76-c8aeb8cd20ae> in <module>
----> 1 t[0] = 1


TypeError: 'tuple' object does not support item assignment

更多详细信息可以参考https://docs.python/3/tutorial/datastructures.html#tuples-and-sequences

多元赋值语法

关于Tuple(当然还有list),事实上还有一种更常见但不怎么被人注意到的用法

def fun():
    return 1, 2, 3, 4  # tuple太常用了,所以很多时候括号()都被省略了


var = fun()
print(var)
print(type(var))
(1, 2, 3, 4)
<class 'tuple'>
var1, var2, var3, var4 = fun()  # 称为unzip
# (var1, var2, var3, var4) = fun()

print(var1)
print(var2)
print(var3)
print(var4)
1
2
3
4

还记得我们在C语言里面那个变量交换内容的操作么?

a = 1
b = 2
print(a, b)
temp = a
a = b
b = temp
print(a, b)
1 2
2 1
a = 1
b = 2
print(a, b)
b, a = a, b
print(a, b)
1 2
2 1

关于dict的key

到这里我们终于可以回来介绍一下dict的key了,
官方对dict的介绍为

A mapping object maps hashable values to arbitrary objects.

而关于hashable,python文档的介绍如下:

An object is hashable if it has a hash value which never changes during its lifetime (it needs a __hash__() method), and can be compared to other objects (it needs an __eq__() method). Hashable objects which compare equal must have the same hash value.

换句话说,作为dict的key使用的值必须是稳定的,能被可靠的索引,而list内容是可变(mutable)的,所以不能作为dict的key。

在python中,所有immutable的对象,包括所有数值类型,字符串,tuple都是immutable的,所以可以被当作dict的key。

对字符串的替换,拼接等操作,都会返回一个新的字符串,而不是在原有字符串上进行操作(inplace方式)

而用户定义的类型,如果其实现了__hash__方法,可以提供一个稳定的hash value用以区别两个不同的object,那么这个用户类型也可以作为dict的key,即使这个用户类型内部的元素会改变,本身是mutable的。

python 高级

闭包closure

内层函数对外层函数非全局变量的引用,就叫做闭包函数。闭包会一直存在内存当中,不会因为函数执行结束而被释放。
设想如下情景,我们需要一个函数,这个函数可以记住自己被调用了多少次,该如何编写这个函数。
当然,可以用全局变量来实现,也可以考虑用后面的class来实现,但是这两种方式都需要引入额外的东西。

def fun_gen():  # 生成函数的函数
    count = 0

    def fun(var):
        nonlocal count  # 声明,引用外部函数的变量,不加这个会报错
        print("got {}".format(var))
        count += 1
        print("invoked {} times".format(count))

    return fun


fun = fun_gen()

fun(1)
fun("duck")
fun("I am iron man!")
got 1
invoked 1 times
got duck
invoked 2 times
got I am iron man!
invoked 3 times

decorator/装饰器

上面已经看完了闭包,那么我们接下来看一个有点让人头疼的东西,考虑这样一个应用场景,我想计算一下某个函数调用总共花了多少时间,该怎么办?

import time


def factorial(num):
    """
    阶乘函数,这里使用了递归的方式    
    """
    if num > 1:
        return num * factorial(num - 1)
    else:
        return num


def compute(num=128):
    factorial(num)  # 其实这个函数计算的很快

下面是一个很简单的实现方式,但是说实话这种方式很麻烦,而且容易出错。

start = time.time()
compute(1024)  # 计算而不输出,太大了,输出很占地方。
period = time.time() - start
print("used:[{}]".format(period))
used:[0.001203298568725586]

下面做一个decorator来实现这个功能

def trace(fun):
    """
    这个就是我们要用的decorator
    """
    print("will trace:[{}]".format(fun))

    def wrapper(*args, **kwargs):
        """
        这个是decorator的返回值,一般是一个函数
        事实上,这个返回的函数,会被用来代替被decorate的函数被调用

        因此,这个函数需要能处理任何参数输入,因为很多时候,一个decorator并不知道,
        要被decorate的函数是什么样子,接收什么样的参数,而我们前面见过的*参数就能
        很好解决这个问题
        """
        start = time.time()

        var = fun(*args, **kwargs)
        # 在这个函数内部调用被decorate的函数
        # 可以看出来,这里的fun是最外面传进来的,很典型的闭包

        period = time.time() - start
        print("used:[{}]".format(period))

        return var  # 不要忘了处理fun的返回值。

    return wrapper

到这里,我们的decorator已经定义好了,其实可以看出来,这个东西略微有点复杂。

定义好了怎么用呢,decorator有两种用法,一种如下比较直观:

compute_wrapped = trace(compute)
# 这里的compute_wrapped其实就是trace里面的wrapper
print("#" * 10)
ret = compute_wrapped(1024)
# 调用compute_wrapped就是调用wrapper,wrapper内部再调用compute
will trace:[<function compute at 0x7f84d7af89e0>]
##########
used:[0.0006742477416992188]

另外一种写法很简洁,而且也是推荐使用的方式,但是不太好理解,它是在定义函数的时候就直接使用了:

@trace
def compute(num):
    """
    这里的我们要定义的是compute,实际定义的也确实是compute
    但是经过trace的decorate,在运行空间里面的compute就被替
    换成了trace里面的wrapper,所以最终调用compute的时候,实
    际调用的是trace里面的wrapper
    """
    factorial(num)


print("#" * 10)
ret = compute(1024)
will trace:[<function compute at 0x7f84d7af63b0>]
##########
used:[0.0005753040313720703]

有时候,仅仅能够在函数调用前后做一些操作还不够,我们还希望我们的decorator可以接受一些参数,表现一些不同的行为,这种时候,上述的trace就做不到,我们需要一个更复杂的decorator:

def trace(tag="tag", flag=True):
    """
    这个就是我们要用的带参数的decorator
    """
    print("got vars tag:[{}] flag:[{}]".format(tag, flag))

    def decorator(fun):
        """
        这个decorator其实跟刚才上面定义的那个不带参数的trace最外层是一样的
        """
        if flag:
            print("will trace:[{}]".format(fun))
        else:
            print("will not trace:[{}]".format(fun))

        def wrapper(*args, **kwargs):
            """
            这个是decorator的返回值,一般是一个函数
            事实上,这个返回的函数,会被用来代替被decorate的函数被调用

            因此,这个函数需要能处理任何参数输入,因为很多时候,一个decorator并不知道,
            要被decorate的函数是什么样子,接收什么样的参数,而我们前面见过的*参数就能
            很好解决这个问题
            """
            print("inside trace the tag is:[{}]".format(tag))
            if flag:
                start = time.time()

            var = fun(*args, **kwargs)
            # 在这个函数内部调用被decorate的函数
            # 可以看出来,这里的fun是外面传进来的,很典型的闭包

            if flag:
                period = time.time() - start
                print("used:[{}]".format(period))

            return var  # 不要忘了处理fun的返回值。

        return wrapper

    return decorator

上面这个东西看起来有点复杂,不着急,我们先来一步步的用一下这个东西看看。

def compute(num=128):
    """
    注意,到这里,这个compute需要重新定义一遍,不然我们用的还是上面那个被decorate的版本
    """
    factorial(num)


deco = trace(tag="trace")  # 这里是传一个参数,返回的是trace里面的decorator
print("#" * 10)
compute_decorated = deco(compute)  # 这里跟之前的一样,其实返回的是wrapper

print("#" * 10)
compute_decorated(1024)  # 这里实际调用的是warpper,其内部调用的compute
got vars tag:[trace] flag:[True]
##########
will trace:[<function compute at 0x7f84d7af6050>]
##########
inside trace the tag is:[trace]
used:[0.0006210803985595703]

上面的代码,一步步对trace的调用过程做了解析,但是很繁琐,其实这里这个带参数的trace可以这么用:

@trace(tag="trace_2")
def compute(num=128):
    factorial(num)


print("#" * 10)
compute(1024)
got vars tag:[trace_2] flag:[True]
will trace:[<function compute at 0x7f84d7af6680>]
##########
inside trace the tag is:[trace_2]
used:[0.0005922317504882812]
@trace(tag="trace_2", flag=False)
def compute(num=128):
    factorial(num)


print("#" * 10)
compute(1024)  # flag给了一个False。这里就不再trace了
got vars tag:[trace_2] flag:[False]
will not trace:[<function compute at 0x7f84d7af6320>]
##########
inside trace the tag is:[trace_2]

Classes

Python提供面向对象的支持,在python中定义一个类很简单

面向对象的内容比较庞大,这里仅介绍部分关键内容

class Greeter:
    member = "Greeter"  # 类属性
    # 构造器
    def __init__(self, name):
        self.name = name  # 实例属性

    # 实例方法
    def greet(self, loud=False):
        """
        这里的self是调用这个方法的本类的实例
        """
        print("self is :[{}]".format(self))
        if loud:
            print("HELLO, %s!" % self.name.upper())
        else:
            print("Hello, %s" % self.name)

    # 类方法,需要通过classmethod进行定义
    @classmethod  # 这是一个装饰器/decorator的用法
    def Greet(cls, loud=True):
        """
        这里的cls是调用这个方法的类
        """
        print("cls is :[{}]".format(cls))
        if loud:
            print("HELLO, %s!" % cls.member.upper())
        else:
            print("Hello, %s" % cls.member.name)


g = Greeter("Fred")
g.greet()
g.greet(loud=True)

Greeter.Greet()  # 类的方法可以通过类来调用,也可以通过实例调用
g.Greet()

print(g.member)  # 类属性可以通过实例或者类本身读取
print(Greeter.member)

g.member = "greeter"
# 但是直接在实例上赋值会创建一个新的实例属性,而不是修改类属性的值
# 所以如果用实例调用类方法,而里面又涉及了对类属性的修改,就会出问题
print(g.member)
print(Greeter.member)
self is :[<__main__.Greeter object at 0x7f84d7a1b490>]
Hello, Fred
self is :[<__main__.Greeter object at 0x7f84d7a1b490>]
HELLO, FRED!
cls is :[<class '__main__.Greeter'>]
HELLO, GREETER!
cls is :[<class '__main__.Greeter'>]
HELLO, GREETER!
Greeter
Greeter
greeter
Greeter

需要注意的是,这里的self 不是一个很特别的变量,只是约定俗成的写法,self也不是python的保留关键字,你可以用任何变量名替代self。

class Greeter2:
    # Constructor
    def __init__(this, name):
        this.name = name

    def greet(this, loud=False):
        if loud:
            print("HELLO, %s!" % this.name.upper())
        else:
            print("Hello, %s" % this.name)


g = Greeter2("Fred")
g.greet()
g.greet(loud=True)
Hello, Fred
HELLO, FRED!

在python中,甚至类本身的定义都是可以随时被修改的。

class Greeter3:
    # Constructor
    def __init__(this, name):
        this.name = name


# 独立于类定义的一个函数定义
def greet(this, loud=False):
    if loud:
        print("HELLO, %s!" % this.name.upper())
    else:
        print("Hello, %s" % this.name)


Greeter3.greet = greet  # 函数的定义可以在运行的时候被替换
g = Greeter3("Greeter3")
g.greet()
g.greet(loud=True)
Hello, Greeter3
HELLO, GREETER3!

class的继承与重载

既然涉及面向对象,那么就一定有类的继承与重载。下面来定义四个类,Base是基类,A,B是Base的子类,Derived同时继承AB,是AB的子类,关系如下:

类继承关系 A Derived B Base
class Base:
    """
    这里的多行字符串,也是python的一个注释,可以被工具自动提取作为class的说明文档
    """

    def __init__(self, name="Base"):
        """
        class的method注释跟function注释是完全一样的规则
        """
        print("init Base")
        self.name = name

    def fun(self):
        print("fun from Base [{}]".format(self.name))

    def fun_2(self):
        print("fun_2 from Base [{}]".format(self.name))

    def funBase(self):
        print("funBase from Base [{}]".format(self.name))


class A(Base):
    def __init__(self, name="A"):
        super().__init__(name=name)
        # 如果这里的super init不加name会怎么样呢?
        print("init A")

    def fun(self):
        super().fun()
        print("fun from A [{}]".format(self.name))

    def fun_2(self):
        print("fun_2 from A [{}]".format(self.name))

    def funA(self):
        print("funA from A [{}]".format(self.name))


class B(Base):
    def __init__(self, name="B"):
        super().__init__(name=name)
        print("init B")

    def fun(self):
        super().fun()
        print("from B [{}]".format(self.name))

    def fun_2(self):
        super().fun_2()
        print("fun_2 from B [{}]".format(self.name))

    def funB(self):
        print("funB from B [{}]".format(self.name))


class DerivedA(A, B):
    def __init__(self, name="DerivedA"):
        # super(DerivedA, self).__init__(name=name)
        super().__init__(name=name)  # 也可以省略,作用一样
        print("init DerivedA")

    def fun(self):
        super().fun()
        print("fun from DerivedA[{}]".format(self.name))

    def fun_2(self):
        super().fun_2()
        print("fun_2 from DerivedA [{}]".format(self.name))
da = DerivedA()
init Base
init B
init A
init DerivedA

这里,对子类的实例化,会沿着继承链从下到上, 从左到右(子类继承括号的顺序)的顺序查找基类,然后按照相反的方式进行实例化

# 可以使用super来调用父类的被重载的函数
# 一定注意继承链的查找顺序
da.fun()
fun from Base [DerivedA]
from B [DerivedA]
fun from A [DerivedA]
fun from DerivedA[DerivedA]
# 中间基类每一层都要调用super函数,
# 不然继承链会在没有调用super的那一层断掉
# 对init是如此,对普通函数也是如此
da.fun_2()
print("#" * 10)

da.funA()
print("#" * 10)

da.funB()
print("#" * 10)

da.funBase()
fun_2 from A [DerivedA]
fun_2 from DerivedA [DerivedA]
##########
funA from A [DerivedA]
##########
funB from B [DerivedA]
##########
funBase from Base [DerivedA]

除了super方式之外,其实还有下述方式,但是下述方式有个明显的问题

class Base:
    def __init__(self):
        print("init Base")


class A(Base):
    def __init__(self):
        Base.__init__(self)
        print("init A")


class B(Base):
    def __init__(self):
        Base.__init__(self)
        print("init B")


class DerivedA(A, B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)
        print("init DerivedA")


da = DerivedA()
init Base
init A
init Base
init B
init DerivedA

上面的Base,被调用了两次,所以除非确实需要,不要这么做。使用super能满足大多数场景下的需求。

Magic Method/魔术方法

到这里,带双下划线的方法已经出现过很多次了,这里正式介绍一下。

Python中,所有以“__”双下划线包起来的方法,都统称为“Magic Method”,中文称魔术方法。

同时还存在“__”双下划线开头的成员,又是另外的用途,待会儿单独介绍。

Magic method存在于类中,一般作为实例方法,其与特定的python内建函数或特定操作对应,一般都有特定的用途。

换句话说,python中有一批Magic Method,其名字和用途是已经确定了的,我们只需要根据自己的需要,实现这些MagicMethod即可,python的一些语法和内建函数会自动依赖这些MagicMethod来运行。

已经介绍过的__init__就是比较常见的一个,与之对应的是__del__这两个函数分别用于实例的初始化和删除,也就是其他面向对象语言中的构造器和析构器。

python中有比较完善的垃圾回收机制,所以很多时候不需要使用析构器。虽然很多文献指出这个垃圾回收机制的效率不高(甚至有人指出,禁用垃圾回收能提高python的运行效率)。

前面已经用过的len函数,依赖于 __len__,而dict的key必须实现__hash__等。

关于Magic Method,可以使用dir这个内建函数去查询需要模仿实现的类型,里面都有什么方法。例如:

print(dir(list))
print(help(list.__add__))
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
Help on wrapper_descriptor:

__add__(self, value, /)
    Return self+value.

None

这里做个有意思的小程序,对于这个自定的class,运行len会返回当前系统的unix timestamp

import datetime


class my_len:
    def __len__(self):
        return int(datetime.datetime.now().timestamp())


# python里面的unixtimestamp包含毫秒,但是len只能处理整数

print(len(my_len()))
1599139426

当然这里的实现其实很不好,因为这种方式很奇怪,返回的数据让人很困惑.

权限控制

提到面向对象,那么一定有对被访问对象的访问权限控制相关的功能,如public,protected,private等。

而这里是python比较特别的地方,python没有提供相关的机制,python的作者认为类的设计和使用者应该进行充分的沟通,而不是依赖这种语法设计来保证访问权限。

但是Python里面还是提供了一个替代方案来实现私有成员。在python中,以“__”双下划线开头的成员是私有成员,外部调用者不能直接访问,注意这里提了一个直接,事实上,想访问还是有办法的。

class A:
    def __init__(self):
        self.__var1 = 1
        self.__var2 = 2


a = A()

print(a.__var1)
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-101-c9800fd69d01> in <module>
      7 a = A()
      8 
----> 9 print(a.__var1)


AttributeError: 'A' object has no attribute '__var1'
print(a._A__var1)
1

Property

虽然 Python 没有提供语言定义级别的,很好用的访问控制的方式,但是提供了一个很有意思的 property 可以用来控制 Class内部对象的访问。

class C:

    def __init__(self):
        self._x = "The managed attribute"

    def getx(self):
        print("getting the attribute")
        return self._x

    def setx(self, value):
        print("setting the attribute")
        self._x = value

    def delx(self):
        print("deleting the attribute")
        del self._x

    x = property(getx, setx, delx, "I'm the 'x' property.")


c = C()
print(c.x)
c.x = 123

print(c.x)
del c.x
getting the attribute
The managed attribute
setting the attribute
getting the attribute
123
deleting the attribute

可以看到,这里的 x 其实是一个类属性,但是这个 property 的用法,是在让人觉的不是很舒服,那么我们换一种写法。

class C:

    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        print("getting the attribute")
        return self._x

    @x.setter
    def x(self, value):
        print("setting the attribute")
        self._x = value

    @x.deleter
    def x(self):
        print("deleting the attribute")
        del self._x
        
c = C()
print(c.x)
c.x = 123

print(c.x)
del c.x
getting the attribute
None
setting the attribute
getting the attribute
123
deleting the attribute

上面的代码,看上去很像前面说的 decorator, 其实这里的 property 就是一个内建的 decorator, 专门提供来做类内属性管理的。

Generator生成器

python的for语法和容器配合的方式,非常的直观好用,而在这背后支撑一切的,则是generator的使用。

大部分python开发人员差不多每天都在用generator,但是自己却没有意识到。

在介绍generator之前,先来看一下如果不使用for循环的情况下访问list会怎样。

l = [1, 2, 3, 4]


it = iter(l)
# it = l.__iter__()  # 或者可以用magic method

print(it)
print(next(it))
print(it.__next__())  # 也可以调用一个magic method,效果是一样的。
print(next(it))
print(next(it))

print(next(it))  # 已经访问完毕了,继续访问则会报错。
<list_iterator object at 0x7f84d7b0b390>
1
2
3
4



---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

<ipython-input-105-21c8eea4dd50> in <module>
     11 print(next(it))
     12 
---> 13 print(next(it))  # 已经访问完毕了,继续访问则会报错。


StopIteration: 

这里其实就是一个iterator,迭代器的用法,如果学过C++的话,会对迭代器的概念比较熟悉。

其实比较关键的就是__iter__和__next__这两个MagicMethod的实现。

class my_List(object):
    def __init__(self):
        self.l = [1, 2, 3, 4]
        self.l.reverse()  # 这里用的pop,是从后面开始的

    def __iter__(self):
        return self

    def __next__(self):
        if len(self.l) > 0:
            return self.l.pop()
        else:
            raise StopIteration


it = my_List().__iter__()
# it = iter(my_List()) # 或者可以用magic method

print(it)
print(next(it))
print(it.__next__())  # 也可以调用一个magic method,效果是一样的。
print(next(it))
print(next(it))

print(next(it))  # 已经访问完毕了,继续访问则会报错。
<__main__.my_List object at 0x7f84d7b16a90>
1
2
3
4



---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

<ipython-input-106-bbca281fb828> in <module>
     23 print(next(it))
     24 
---> 25 print(next(it))  # 已经访问完毕了,继续访问则会报错。


<ipython-input-106-bbca281fb828> in __next__(self)
     11             return self.l.pop()
     12         else:
---> 13             raise StopIteration
     14 
     15 


StopIteration: 
# 也可以使用for来处理
l = my_List()
for i in l:
    print(i)
1
2
3
4

可以看到,这里的自定方式,实现的这个my_List在特定情况下可以当作List使用,但是这种语法有些繁琐,因此python中专门提供了generator生成器语法。

def my_list():
    for i in [1, 2, 3, 4]:
        yield i  # generator看上去就像是一个普通的函数,只不过把return替换成了yield


# 每次执行到yield的时候,函数会返回一个值,但是并不结束
# 而是把中间状态都保存起来,只要后续还有数据就可以继续访问

l = my_list()

for i in l:
    print(i)


it = my_list().__iter__()
# it = iter(l) # 或者可以用magic method

print(it)
print(next(it))
print(it.__next__())  # 也可以调用一个magic method,效果是一样的。
print(next(it))
print(next(it))

print(next(it))  # 已经访问完毕了,继续访问则会报错。
1
2
3
4
<generator object my_list at 0x7f84d7afac50>
1
2
3
4



---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

<ipython-input-108-7ccc555d3398> in <module>
     22 print(next(it))
     23 
---> 24 print(next(it))  # 已经访问完毕了,继续访问则会报错。


StopIteration: 

异常处理

对于一个面向对象的通用编程语言来说,异常处理也是一个重要的部分。

python的异常处理使用try except的语法.

a = 0
b = 0
try:
    c = a / b
except:  # 最简单的语法,知道出了错误,但是不知道错误是啥
    print("failed, but I dont't known why!")
failed, but I dont't known why!
try:
    c = a / b
except Exception as ex:  # 最宽泛的exception捕获可以用Exception这个类来获取具体信息
    print(
        "failed, don't know what type this exception is but I can get the detail:[{}]".format(
            ex
        )
    )
failed, don't know what type this exception is but I can get the detail:[division by zero]
try:
    c = a / b
except ZeroDivisionError:  # 明确知道错误是啥,但是不知道细节
    print("failed, caused by ZeroDivisionError, but I don't know details!")
failed, caused by ZeroDivisionError, but I don't know details!
try:
    c = a / b
except ZeroDivisionError as ex:  # 可以从错误中获取细节
    print("failed, caused by ZeroDivisionError, details:[{}]".format(ex))
failed, caused by ZeroDivisionError, details:[division by zero]
try:
    c = a / NotExist
except ZeroDivisionError as ex:
    print("failed, caused by ZeroDivisionError, details:[{}]".format(ex))
except:  # except 可以有多层
    print("failed, not caused by ZeroDivisionError, I don't know why!")
failed, not caused by ZeroDivisionError, I don't know why!
try:
    c = a / NotExist
except ZeroDivisionError as ex:
    print("failed, caused by ZeroDivisionError, details:[{}]".format(ex))
except NameError as ex:
    # except可以有好多层, 哪一层匹配到了这个错误就执行哪一层的代码
    # 如果多层之间有继承关系,则被继承的exception一定放在后面
    print("failed, caused by NameError, details:[{}]".format(ex))
failed, caused by NameError, details:[name 'NotExist' is not defined]
try:
    c = a / NotExist
except ZeroDivisionError as ex:
    print("failed, caused by ZeroDivisionError, details:[{}]".format(ex))
except NameError as ex:
    print("failed, caused by NameError, details:[{}]".format(ex))
except:  # 如果前面几层都没匹配到,那么就执行这个最宽泛的exception捕获
    print("failed, not caused by ZeroDivisionError or NameError, I don't know why!")
finally:  # 不管异常有没有执行,都可以放一些语句在这里执行,例如数据库关闭代码可以放这里
    print("failed or not, this line will be executed!")
failed, caused by NameError, details:[name 'NotExist' is not defined]
failed or not, this line will be executed!
try:
    raise OSError  # 可以用raise抛一个异常
except ZeroDivisionError as ex:
    print("failed, caused by ZeroDivisionError, details:[{}]".format(ex))
except NameError as ex:
    print("failed, caused by NameError, details:[{}]".format(ex))
except:
    print("failed, not caused by ZeroDivisionError or NameError, I don't know why!")
finally:
    print("failed or not, this line will be executed!")
failed, not caused by ZeroDivisionError or NameError, I don't know why!
failed or not, this line will be executed!
try:
    raise OSError("don't know what to do, how about raise a exception")  # 可以用raise抛一个异常
except ZeroDivisionError as ex:
    print("failed, caused by ZeroDivisionError, details:[{}]".format(ex))
except NameError as ex:
    print("failed, caused by NameError, details:[{}]".format(ex))
except Exception as ex:  # 最宽泛的exception捕获可以用Exception这个类来获取具体信息
    print(
        "failed, don't know what type this exception is but I can get the detail:[{}]".format(
            ex
        )
    )
finally:
    print("failed or not, this line will be executed!")
failed, don't know what type this exception is but I can get the detail:[don't know what to do, how about raise a exception]
failed or not, this line will be executed!
try:
    c = int("ttt")
except ZeroDivisionError as ex:
    print("failed, caused by ZeroDivisionError, details:[{}]".format(ex))
except NameError as ex:
    print("failed, caused by NameError, details:[{}]".format(ex))
except:
    print("failed, not caused by ZeroDivisionError or NameError, I don't know why!")
    print("can't handle this error, so I decided to raise it again!")
    raise  # 有的时候,静默处理exception是不可取的,这里记录了一下,然后又重新把这个异常抛了出来
finally:
    print("failed or not, this line will be executed!")
failed, not caused by ZeroDivisionError or NameError, I don't know why!
can't handle this error, so I decided to raise it again!
failed or not, this line will be executed!



---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-119-0b90b51dbaea> in <module>
      1 try:
----> 2     c = int("ttt")
      3 except ZeroDivisionError as ex:
      4     print("failed, caused by ZeroDivisionError, details:[{}]".format(ex))
      5 except NameError as ex:


ValueError: invalid literal for int() with base 10: 'ttt'
# 默认的exception打印,获取的信息有限,调用一些内部的库,可以获得更多的信息.
import sys
import traceback

try:
    raise ValueError("this is a exp")
except Exception as ex:
    ex_type, ex_val, ex_stack = sys.exc_info()
    print(ex_type)
    print(ex_val)
    print(ex.args)
    for stack in traceback.extract_tb(ex_stack):
        print(stack)
<class 'ValueError'>
this is a exp
('this is a exp',)
<FrameSummary file <ipython-input-120-ed02f5e8aa21>, line 6 in <module>>

更多关于异常处理的内容可以参考这里https://docs.python/3/tutorial/errors.html

python内建了很多exception,这里列举如下,每个exception的详细信息请参考这里:
https://docs.python/3/library/exceptions.html

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

文件IO

作为一门通用编程语言,python也有相应的IO实现,以下是python读写文件的一些示例

f = open("test.txt", mode="w")  # w 是写入的意思,如果文件已经存在,会被清空
f.write("123\n")  # 这个write是不会自带换行的
f.write("456\n")
f.writelines(["1", "2", "3", "4", "5"])
f.close()  # 打开的文件一定要关闭
f = open("test.txt")  # mode默认是r,这里省略了
print(f.read())  # 一次把所有东西都打印出来
f.close()
123
456
12345
f = open("test.txt")  # mode默认是r,这里省略了
print(f.readlines())  # 读取所有的行,以列表形式返回
f.close()
['123\n', '456\n', '12345']
f = open("test.txt")  # mode默认是r,这里省略了
for l in f:
    print(l)  # 还可以用for in 的方式访问文件,注意print会默认添加一个换行,而l也自带换行
f.close()
123

456

12345
f = open("test.txt", mode="a")  # a是添加,不会清空现在文件的内容
f.write("abc\n")  # 这个write是不会自带换行的
f.write("def\n")
f.writelines(["a", "b", "c", "d", "e"])
f.close()  # 打开的文件一定要关闭
f = open("test.txt")  # mode默认是r,这里省略了
print(f.read())  # 一次把所有东西都打印出来
f.close()
123
456
12345abc
def
abcde
f = open("test.txt", encoding="ascii")  # mode默认是r,这里省略了
print(f.read())  # 一次把所有东西都打印出来
f.close()
123
456
12345abc
def
abcde
f = open("test.txt", mode="wb")  # 文件默认是文本默认打开,也可以用二进制模式打开
f.write("abc\n".encode())  # 二进制文件只能接受二进制写入
f.write("def\n".encode())
f.writelines([b"a", b"b", b"c", b"d", b"e"])
f.close()  # 打开的文件一定要关闭
f = open("test.txt", mode="rb")  # 文件以二进制读取
print(f.read())  # 读出来的就都是二进制数据
f.close()
b'abc\ndef\nabcde'

打开的文件一定要关闭,不然会出现未知的问题,同时在程序运行中,文件会一直处于被占用状态.

在小型程序中,文件的访问影响比较小,如果没有小心处理文件的关闭,可能也不会造成太大的问题,但是复杂大型系统里面,文件处理不好会是极大的隐患.

上述文件open/close的逻辑多少有些繁琐,而且文件的打开关闭不在同一个地方,打开了忘记关闭是在所难免的.因此,python中也有with语法,可以自动的处理文件的打开和关闭,在with语句中的文件,直接使用即可,不再需要手动调用close,处理关闭问题.极大的方便了文件的处理.

with open("test.txt", mode="rb") as f:
    print(f.read())
b'abc\ndef\nabcde'

这里介绍的文件操作比较基础,远不是python中文件IO的全部,但是像其他编程语言一样,我们很少有机会直接处理IO操作,大都是通过相应的库来处理特定格式的文件操作.所以python的IO,更多的是跟各种库打交道

context manager

可以看到,上述的IO操作中,with语法可以极大的简化代码。在python中,此种语法称作context manager。

一个context manager中,最重要的是__enter__和__exit__两个方法,这个两个方法分别用于处理资源的初始化和资源的清理,实现这两个方法并适当的处理资源就可以。

class my_open:
    def __init__(self, path, suppress=False):
        self.path = path
        self.suppress = suppress

    def read(self):
        return self.f.read()

    def fail(self):
        print("try raise a error")
        raise ValueError

    def __enter__(self):
        """
        enter 可以返回self或者其他什么东西
        在with语法中,如果有as,那么这里返回的内容会被绑定到as后面的变量上面
        """
        print("starting with block")
        self.f = open(self.path)
        return self

    def __exit__(self, exc_type, exc_value, exc_tb):
        """
        如果在context manager运行的过程中出现了异常,那么异常会被传入exit作为判断的依据
        exit返回False则认为context manager失败了,这些异常会被直接重新抛出
        但是如果exit里面处理了这些异常,而且返回了一个True,那么这些异常将不会再向外传递
        """
        self.f.close()

        if exc_type is None:
            print("exited normally\n")
        else:
            print("raise an exception! " + str(exc_type))

        return self.suppress


with my_open("test.txt") as f:
    print(f.read())
starting with block
abc
def
abcde
exited normally
try:
    with my_open("test.txt") as f:
        f.fail()
except ValueError:
    print("ValueError raised")
starting with block
try raise a error
raise an exception! <class 'ValueError'>
ValueError raised
try:
    with my_open("test.txt", suppress=True) as f:
        f.fail()
except ValueError:
    print("ValueError raised")
starting with block
try raise a error
raise an exception! <class 'ValueError'>

很多时候,自己写一个context manager是很麻烦的,所以python中提供了contextlib来处理context manager的编写。详情请参考contextlib的文档。

事实上,大部分的场景使用各种库的context manager就好了,很少会碰到需要自己编写的情况。

map,filter,reduce

这里介绍三个比较有趣的python内建操作,这三个操作经常与容器,lambda等联合使用,处理序列数据.

回忆前面提到的list comphension,对于一个list,我们可以很方便的对其进行操作,例如求其每个元素的平方.

l = [1, 2, 3, 4, 5, 6, 7, 8, 9]
l_square = [e ** 2 for e in l]
print(l_square)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

换一个方式可以这么做:

# 注意map返回的是一个map object,需要用list去run一下
l_square = list(map(lambda e: e ** 2, l))
print(l_square)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

结果是一样的

map接受的函数对senquence的每个元素操作并返回操作后的结果,map本身产生一个generator,用list可以取得generator的结果

那么如果我要把这个list里面,所有的偶数找出来,该怎么做呢?

# 这是最简单的思路,用for循环
l_square = []
for e in l:
    if e % 2 == 0:
        l_square.append(e)
print(l_square)
[2, 4, 6, 8]
# 这是高级一点的,python方式.用comprehension
l_square = [e for e in l if e % 2 == 0]
print(l_square)
[2, 4, 6, 8]
# 这是这里要介绍的filter,注意filter返回的是一个filter object,需要用list去run一下
l_square = list(filter(lambda e: e % 2 == 0, l))
print(l_square)
[2, 4, 6, 8]

当然,看上述的例子,fitler方式的字符数量比comprehension的方式要多,这个例子里面,用filter并不那么合适.

filter接受的函数对senquence的每个元素进行判断,并返回判断值,filet收集这些判断,并根据判断给出一个generator,这个generator用list获取之后,剩下的是filter接受的函数,返回True的那些元素

再接着上面的例子,对于所有元素求和,该怎么做呢?

print(sum(l))  # 这其实是最简洁的方式
from functools import reduce  # 相比于map filter,reduce没那么常用,所以被放在了functools里面

print(reduce(lambda x, y: x + y, l))
45
45

reduce最特殊,他会接受一个有两个输入的函数,两个输入会在这个函数里面进行一定的运算,返回一个值,而这个值会被用来跟这个sequence的下一个值继续进行运算,直到所有的senquce元素都被处理完,reduce不会返回generator,而是直接返回最终值

这三个函数,都是用给定的函数对senquence(iterable的东西)进行操作.

help(map)
help(filter)
help(reduce)
Help on class map in module builtins:

class map(object)
 |  map(func, *iterables) --> map object
 |  
 |  Make an iterator that computes the function using arguments from
 |  each of the iterables.  Stops when the shortest iterable is exhausted.
 |  
 |  Methods defined here:
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __next__(self, /)
 |      Implement next(self).
 |  
 |  __reduce__(...)
 |      Return state information for pickling.
 |  
 |  ----------------------------------------------------------------------
 |  Static methods defined here:
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.

Help on class filter in module builtins:

class filter(object)
 |  filter(function or None, iterable) --> filter object
 |  
 |  Return an iterator yielding those items of iterable for which function(item)
 |  is true. If function is None, return the items that are true.
 |  
 |  Methods defined here:
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __next__(self, /)
 |      Implement next(self).
 |  
 |  __reduce__(...)
 |      Return state information for pickling.
 |  
 |  ----------------------------------------------------------------------
 |  Static methods defined here:
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.

Help on built-in function reduce in module _functools:

reduce(...)
    reduce(function, sequence[, initial]) -> value
    
    Apply a function of two arguments cumulatively to the items of a sequence,
    from left to right, so as to reduce the sequence to a single value.
    For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
    ((((1+2)+3)+4)+5).  If initial is present, it is placed before the items
    of the sequence in the calculation, and serves as a default when the
    sequence is empty.

鉴于reduce可能不太好理解,我们再来看一个例子.

start = "characters of input string are:"
string = "hello world"

print(reduce(lambda x, y: x + "[{}],".format(y), string, start))
characters of input string are:[h],[e],[l],[l],[o],[ ],[w],[o],[r],[l],[d],

多线程,多进程

GIL

在介绍python的多线程,多进程之前,首先来了解一下python里面最大的一个坑,也是最为其他语言和框架的使用者诟病的一点:GIL(global interpreter lock):

python是一个语言的规范,目前广泛使用的的是一种叫做CPython的实现。CPython实现的python解释器里面有个全局解释器锁(Global Interpreter Lock)GIL,这个东西本身并不是python规范里面的东西,所以罪魁祸首不是python,而是CPython。CPython的市场最大,所以一般提python就是CPython,本文未加区别。其他一些实现如JPython(跑在JVM上)或者IronPython(跑在.Net上)没有GIL问题。

目前CPU都是多核的,为了利用多核,就出现了多线程技术。而多线程技术处在同一个进程中,数据为所有线程共有,所以数据同步是一个非常严峻的问题。python中为了简化这个问题,引入了一个非常navie的方案,就是全局加锁,interpreter在执行代码的时候,同一时刻,只执行一个线程的代码,并且规定一个线程的代码在执行一段时间后,必须释放interpreter,切换到另一个线程执行。所以很多时候可以认为python是单线程执行的。同时由于获取释放锁的开销,导致了python的多线程性能在CPU密集的任务中表现极其糟糕。

当然只是在CPU密集的任务上,IO密集的任务由于IO操作不涉及线程切换,所以使用多线程是可以有效的提高效率的。
同时,python的多进程是开启了多个进程,每个进程都有一个python的interpreter,所以能够充分的利用多核提升CPU密集型任务的运算性能.

GIL是一个历史遗留问题,很多库都有意无意的利用了GIL,大大简化了多线程代码的设计。库太多导致GIL暂时没有很好的解决方案。需要注意的是,GIL问题只存在与于python的解释器中,很多操作,如opencv的图片处理,是用C/C++写的,不在GIL的控制范围内,所以没有性能问题。

Tensorflow/pyTorch/mxnet等深度学习框架,其compute engine也是C++实现的,本身不再python的程序空间内,所以也没有GIL的性能问题.

python自带的multiprocessing库提供了很好的对多线程/多进程的支持,而且简单易用.

尝试计算

def count(num):
    """
    计算从0到num的所有整数的和
    """
    var = 0
    for i in range(num + 1):
        var = var + i
    return var


# 把线程换成进程就是下面的操作
from multiprocessing import Pool as ProcessPool  # 多进程执行
from multiprocessing.dummy import Pool as ThreadPool  # 多线程执行,dummy是伪多进程也就是多线程

p = ThreadPool()  # 创建一个线程池
results = p.map(count, range(10))  # 把任务放入线程池
p.close()  # 关闭线程池
p.join()  # 等待线程池的所有任务完成
print(results)  # 取得结果

p = ProcessPool()
results = p.map(count, range(10))
p.close()
p.join()

print(results)
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]

可以看到,python中最方便的多线程,多进程,还是围绕senquence,map等建立的. 其实这种类型的代码,被称为pythonic的代码,其代码比较简洁,而且运行效率也比较高.

上述方案方便是方便,但是有些时候自由度不够,python里面其实也提供了线程基类供继承,也就是threading.Thread

import time
from threading import Thread


class myThread(Thread):
    def run(self):  # 重载这一个函数,把所有的任务逻辑都放在这一个函数里面
        for i in range(10):
            print("child process is working")
            time.sleep(1)


my_thread = myThread()
my_thread.start()  # 不是调用run,而是调用这个start去启动一个线程

print(my_thread.is_alive())
my_thread.join()
# 等待子线程退出,如果没有这一句,那么主线程退出会直接干掉子线程
# 不过我们这里是在一个python交互shell里面,最高主线程会一直存在
print("child process exited")
child process is workingTrue

child process is working
child process is working
child process is working
child process is working
child process is working
child process is working
child process is working
child process is working
child process is working
child process exited

网络与爬虫

因为python语法很简单,而且支持的库生态也足够庞大,所以python的应用领域也很广,目前比较大的一个领域是跟网络有关的,也就是python爬虫.

不过爬虫的内容比较庞大,这里不涉及,有需要可以查询相关爬虫框架的文档.

关于网络,主要涉及socket和web访问,socket推荐使用pyzmq库,详情参考zmq官方文档:http://zguide.zeromq/page:all

web访问推荐使用requests库,详情参考requests官方文档:https://pypi/project/requests/

目前requests的稳定版本是2.x系列,但是2020年pyCon之前requests3会发布,我也不太确定是什么时候.

除非必要,尽量不要尝试自己使用python的内建socket和web访问api,多使用成熟的第三方库.因为python的生态足够庞大,很多时候,一个成熟高效的第三方库可以极大的加快开发的效率.

package和import

当代码比较少的时候,运行一个python shell,一个jupyter,或者单单一个python文件就能解决,一旦系统上了一定的规模,代码量比较大的时候,代码的组织管理就会变成一个足够影响整个项目的开发进度效率,甚至是项目生死的至关重要的问题.

像其他的高级语言一样,python也内建了package管理机制,一般来说,一个py文件就是一个python的module.可以被import,像内建的module一样被调用

一个目录,如果其下包含一个__init__.py文件的话,这个目录就是一个package,目录下的所有py文件都是这个package的成员。目录下如果还有子目录的话,仍然是有__init__.py的为子package,没有的只是普通文件夹。

__init__.py文件可以为空,但是我们一般都在里面放一些初始化的代码。

如果要把这个package作为一个module,直接运行的话,这个目录下,还需要有一个__main__.py文件。

更多关于python模块的内容请参考:https://docs.python/3/tutorial/modules.html#intra-package-references

综合示例

下面我们拿一个比较简单的例子,把前面介绍过的一些内容做一个综合示例。

考虑这样一个应用场景,有一个目录,这个目录里面有多层子目录,每层子目录又包含一些文件,要求用递归函数统计这个目录下面所有文件的尺寸。

下面是代码实现,我们尽量用了多种不同的方式。

import os  # 每个版本都要用os这个模块

path = "/Users/david/Code/Python"
def dir_size(d):
    """
    递归版本.

    这是最基础的版本,没啥好说的,需要注意的是,既然用递归,
    就不要考虑全局变量啥的了,累计的size,直接作为返回值就好了。
    """
    # 读取文件信息
    dlist = os.listdir(d)
    # 遍历所有内容
    allsize = 0
    for i in dlist:
        # 为遍历的文件添加目录
        file = os.path.join(d, i)
        # 判断是否是文件
        if os.path.isfile(file):
            m = os.path.getsize(file)
            allsize += m
        # 判断是否是目录
        if os.path.isdir(file):
            # 调用自己
            allsize += dir_size(file)
    return allsize


print("dir size:\t{}".format(dir_size(path)))
dir size:	120660981
def dir_size(d):
    """
    递归comprehension版本.

    这个是逼格稍微高一点的版本,这里的重点就是comprehension了。
    """
    dlist = [os.path.join(d, i) for i in os.listdir(d)]
    allsize = sum([os.path.getsize(file) for file in dlist if os.path.isfile(file)])

    allsize += sum([dir_size(file) for file in dlist if os.path.isdir(file)])

    return allsize


print("dir size:\t{}".format(dir_size(path)))
dir size:	120660981
def dir_size(d):
    """
    递归comprehension版本.

    另一种蛋疼的写法, 吓唬人玩行,一般别吃饱了撑的用这种写法.
    """
    return sum(
        [
            dir_size(f) if os.path.isdir(f) else os.path.getsize(f)
            for f in [os.path.join(d, i) for i in os.listdir(d)]
        ]
    )


print("dir size:\t{}".format(dir_size(path)))
dir size:	120660981
def dir_size(d):
    """
    递归map filter版本.

    简洁的一逼,刚开始没想起来。
    """
    dlist = [os.path.join(d, i) for i in os.listdir(d)]
    allsize = sum(map(os.path.getsize, filter(os.path.isfile, dlist)))
    allsize += sum(map(dir_size, filter(os.path.isdir, dlist)))

    return allsize


print("dir size:\t{}".format(dir_size(path)))
dir size:	120660981
import os
from multiprocessing.dummy import Pool as ThreadPool


def dir_size(d):
    """
    多线程递归版本.

    1,在这里,这个版本反而比单线程的慢,因为我本地是SSD,而且目录也没有大到离谱,线程切换的
        开销已经完全抵消了多线程的IO效率优势。如果是统计NFS,大规模的磁盘阵列啥的,可能多线程
        会有一些微薄的优势。

    2,因为有递归,所以不能用多进程。python里面的子进程不能再有子进程,会报错的。不要在有递归
        的场合使用多进程,除非小心的设计一下。
        这里其实还有一种思路是先用walk列出所有的文件目录,再用多线程或者多进程统计文件大小,但
        是列出所有目录这个动作本身就要遍历所有文件了,所以这种做法在这个场景下其实效率不高。
    """
    dlist = [os.path.join(d, i) for i in os.listdir(d)]
    allsize = sum([os.path.getsize(file) for file in dlist if os.path.isfile(file)])
    p = ThreadPool()

    ret = p.map(dir_size, [file for file in dlist if os.path.isdir(file)])
    p.close()
    p.join()
    allsize += sum(ret)

    return allsize


print("dir size:\t{}".format(dir_size(path)))
dir size:	120660981
def dir_size(d):
    """
    walk版本.

    这个版本就没有递归了,有点离题,但是递归这个东西本身就有点反人类。
    没想起来怎么把walk的循环用comprehension代替,有兴趣的亲可以试一下。
    """
    allsize = 0
    for r, ds, fs in os.walk(d):
        allsize += sum(
            os.path.getsize(file) for file in [os.path.join(r, f) for f in fs]
        )
    return allsize


print("dir size:\t{}".format(dir_size(path)))
dir size:	120660981

辅助工具

同其他语言一样,python也有一些辅助工具可供代码格式化,代码检查等使用

isort

import 排序工具

pylint, pylama

代码静态检查工具

autopep8

自动代码格式化工具

参考

本教程是dwSun自己写了10年(其实是8年,四舍六入五凑偶得到10年)python的经验,以及参考(也称作剽窃)了下面一些教程写出来的。

  • http://cs231n.github.io/python-numpy-tutorial/
  • https://github/kuleshov/cs228-material/blob/master/tutorials/python/cs228-python-tutorial.ipynb

本文标签: 教程 python