Python
?理由颇多,包括网上传的什么这个时代你必须会编程,Python
编程最简单,人工智能时代来了,不学Python
你会后悔的等等,不否认这些也是当时决定学习Python
的因素,是因为Python
火所以才学的。
五个多月的学习,在Python
方面也算是略有建树,观念也有了一些改变,编程语言毕竟只是工具,Python
确实非常的简介优雅,但是转而一想,我可以学会的东西,其他人也能学会,只会基础语法没有竞争力,所以学习Python
更多的是体会编程的思想,通过学习源码感悟大佬的设计思路。
我的Python
学习是从大一的时候就开始了,那时候还只是一个计算机方面的小白嘛,保持着高中的学习方式,自己买了一本《Python从入门到精通》就开始啃,那时候也是光看不敲,看完就觉得自己学会了,结果出去跟学长探讨的时候发现人家交流的都是什么框架、什么API
,顿感懵逼,才意识自己看的书太浅显了,我缺少一套系统的学习。
后来经大佬推荐了解到B站这个神奇的地方,自学知识的圣地,于是开始在B站上看老男孩的Python全栈系列的教程,但奈何很多教程参差不齐,还是很多年前的东西,有些API
都已经更新了,所以干脆去报名了路飞学城的Python全栈开发就业班,系统的学习Python
开发。
大概花了五个多月吧,把原定六个月的课程学完结课了,期间还掺杂着学校的课程、各种比赛、其它项目开发等,不然还能更快。
当然,学习到的知识自觉还是比较浅显,还得花一段时间沉淀和巩固。
理论上来说,只要有合适的硬件驱动和API,编程语言都是可以做任何开发的,只有合不合适的问题。
先说一下编程语言的老大哥C/C++
吧,这也是我最开始学习的两门语言。C/C++
的特点就是效率高,基本上是所有编程语言里效率最高的,而且一般系统中都具备C/C++
编译器。
目前而言,C
语言主要用来开发底层模块(比如驱动、解码器、算法实现),服务应用(比如web服务器)和嵌入式应用(比如微波炉里的程序)。
当然C++
也可以做这些,不过由于C++
的复杂性和标准问题,程序员还是更愿意使用C
来做。C++
更适合比较复杂但又特别需要高效率的设施,比如大型游戏,一些基础库, 大型桌面应用。
再来聊聊Java
,这也是一门历史悠久的编程语言,可谓是很多Web应用程序、桌面程序、操作系统的立足之本,多年来霸占着编程语言排行榜的榜首,不过最近又被C
给反超了。
Java
具备一些很好的语言特性,以及丰富的框架,在企业应用中最被青睐,主要是它能做的东西太多了,什么游戏、网站之类的都不在话下,在手机领域也占有一席之地,在智能手机爆发之后,Java的手机主场就变成了Android,一度作为Android的标准开发编程语言而存在,不过最近这个称号也是被Kotlin给抢走了。
然后再说PHP
,这是一种被广泛应用的开源通用脚本语言,适用于Web开发并且可嵌入到HTML
中去,它的主要目标就是允许 Web 开发人员快速编写动态生成的 Web 页面,但 PHP的用途远不只于此,PHP还包含了命令列执行接口(command line interface),和产生图形使用者接口(GUI)程式。
目前PHP的应用范围已经相当广泛,尤其是在网页程式的开发上。一般来说PHP大多执行在网页服务器上,通过执行PHP程式码来产生使用者浏览的网页。PHP可以在多数的服务器和操作系统上执行,而且使用PHP完全是免费的。
最后就是重头Python
了,这对初学者来说也是一个入门级编程语言。由于具有丰富和强大的库,它又叫做作胶水语言,能够把用其他语言制作的各种模块(尤其是C/C++)很轻松地联结在一起。
Python 的应用领域非常广泛,分为系统编程,用户图形接口,Internet 脚本,组件集成,数据库编程,快速原型,数值计算和科学计算编程,游戏、图像、人工智能、XML 、机器人编程等等。
最后从编程语言的本质上来总结一下的话,Python、PHP属于解释型语言,C/C++属于编译型语言。
Java这个语言就很神奇了,你可以说它是编译型的。因为所有的Java代码都是要编译的,.java文件不经过编译就什么用都没有。但是你可以说它是解释型的。因为java代码编译后不能直接运行,它是解释运行在JVM上的,所以它是解释运行的,那也就算是解释的了。但是,现在的JVM为了效率,都有一些JIT优化。它又会把.class的二进制代码编译为本地的代码直接运行,所以,又是编译的。
编译型
我们说编译就类似于将一本书全部翻译成机器语言程序,那么编译的过程肯定要消耗一定的时间,而计算机拿到“这本书”之后可以很快的阅读,不仅如此,编译之后的译本还可以给很多能看懂的计算机执行,相比与此,编译所使用的时间就不足为虑了。
1.执行速度快。
对于同一个任务采用不同的语言实现,一般编译型语言的执行速度是解释型语言执行速度的2~10倍。2.调试方便。
编译型语言的代码编写要求比较严谨,在调试时比较方便。3.源码不依赖。
编译型语言执行时不需要源代码。
编译型语言的缺点:
1.代码量大。
完成同一个任务一般编译型语言的代码量是解释型语言代码量的10~50倍。2.平台依赖性。
编译型语言是要基于不同平台底层实现的,因此对平台的依赖性比较大。3.更新代价大。
每次修改完源代码之后都要重新编译。
常见的编译型语言:C、C++、Fortran、Visual Foxpro、Pascal、Delphi、Ada。
解释型
解释型语言的优点:
1.代码简洁。
解释型语言的代码相对编译型语言的代码更为简洁,方便阅读。2.平台独立性和安全性。
解释器内部已经实现了对不同平台的交互处理,这使得解释型语言适合互联网和WEB应用的开发。
解释型语言的缺点:
1.执行速度慢。
相比于编译型语言,解释型语言的执行速度较慢。2.内存占用大。
必须先运行相关的解释器才能执行解释型语言,而解释器会大量消耗资源,占用CPU周期和内存。
常见的解释型语言:Python、Tcl、Perl、Ruby、VBScript、 JavaScript。
**位(bit) **
来自英文bit,音译为“比特”,表示二进制位,位是计算机内部数据储存的最小单位。
字节(Byte)
字节来自英文Byte,音译为“拜特”,习惯上用大写的“B”表示。
字节是计算机中数据处理的基本单位。计算机中以字节为单位存储和解释信息,规定一个字节由八个二进制位构成,即1个字节等于8个比特(1Byte=8bit)。
b --- 比特 位
B --- 字节 1B = 8b
KB --- 千比特 1 KB = 1024B
MB --- 兆比特 1MB = 1024KB
GB --- 吉比特 1GB = 1024MB
PEP8
规范有哪些?Indentation 缩进和换行
每级缩进使用 4 个空格。
如果不想搞乱以前的古老的代码的话,可以使用8个空格长的制表符。
续行应该与其包裹元素对齐,要么使用圆括号、方括号和花括号内的隐式行连接来垂直对齐;要么使用悬挂式缩进对齐。使用悬挂式缩进时,应该考虑下面的意见。第一行不应该有参数;使用缩进以便与其他代码区分清楚。
Yes:
# 与开始的分隔符对齐
foo = long_function_name(var_one, var_two,
var_three, var_four)
# 更多的缩进,更加的清晰
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
No:
# 没有使用垂直对齐时,禁止把参数放在第一行
foo = long_function_name(var_one, var_two,
var_three, var_four)
# 缩进不够,代码不清晰
def long_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
Tabs or Spaces? 制表符或者空格?
绝不要混用制表符和空格。
最流行的 Python 缩进方式是仅使用空格,其次是仅使用制表符。混合着制表符和空格缩进的代码将被转换成仅使用空格。使用带-t选项来调用Python命令行解释器时,代码中非法混用制表符和空格会给出警告。当使用-tt选项时这些警告就会变成错误。高度建议使用这些选项。
对新的项目,强烈建议只使用空格而不是制表符。大多数的编辑器都能轻松做到这一点。
Maximum Line Length 行的最大长度
限制所有行的最大长度为 79 个字符。
周围还有很多设备限制每行 80个字符。而且,限制窗口显示 80 个字符可以并排显示多个窗口。有些设备上,默认折行会打乱代码结构,不易理解。因此,请限制所有行的最大长度为 79 个字符。对一大段长文本(文档字符串或注释),推荐限制每行最多72个字符。
较长代码行折行的首选方法是在圆括号、方括号和花括号内使用Python的隐式续行方式。通过圆括号内的表达式的折行来把较长的代码行折成多行。这种方式要优先使用,优先于反斜杠。同时确保适当的续行缩进。二元运算符的首选的折行处是在运算符之后,而不是之前。
Blank Lines 空行
顶层函数和类之间使用两个空行。
类的方法之间使用一个空行。
(谨慎地)使用额外的空白行来分隔一组相关的函数。一堆相关的单行代码之间的空白行可以省略(例如,一组dummy implementations)。
在函数中使用空行来(谨慎地)表示不同的逻辑段落。
Encodings (PEP 263) 编码
Python核心发行代码应该总是使用 ASCII 或 Latin-1 编码 (又名 ISO-8859-1)。Python 3.0 和以后的版本,首选 UTF-8 而不是 Latin-1,请查看 PEP 3120 获取更多信息。(对中文项目,请首选UTF-8)
Imports 导入
Yes:
import os
import sys
No:
import sys, os
但是可以这么做:
from subprocess import Popen, PIPE
导入应该按照以下的顺序分组:
每组导入之间使用空行隔开。
在导入之后放置任何相关的 __all__ 说明书。
Comments 注释
不好理解的注释不如没有注释。注释要和代码保持与时俱进!
注释应该是一条完整的句子。如果注释是一个短语或句子,它的第一个字应该大写,除非它是一个小写字母开头的标识符(绝对不要改变标识符的大小写)。
如果注释很短,那么结尾的句号可以省略。块注释通常由一个或多个段落组成, 这些段落由完整的句子构成,每个句子都应该使用句号结尾。
句号结尾的句子后面应该有2个空格。
来自非英语国家的Python程序员:请使用英语写注释,除非你120%肯定你的代码将永远不会被不说你的语言的人阅读。
二进制转换成十进制:v = '0b1111011'
十进制转换成二进制:v = 18
八进制转换成十进制:v = '011'
十进制转换成八进制:v = 30
十六进制转换成十进制:v = '0x12'
十进制转换成十六进制:v = 87
# 二进制转换成十进制:v = '0b1111011'
v = '0b1111011'
print(int(v, 2))
# 十进制转换成二进制:v = 18
v = 18
print(bin(v))
# 八进制转换成十进制:v = '011'
v = '011'
print(int(v, 8))
# 十进制转换成八进制:v = 30
v = 30
print(oct(v))
# 十六进制转换成十进制:v = '0x12'
v = '0x12'
print(int(v, 16))
# 十进制转换成十六进制:v = 87
v = 87
print(hex(v))
如 10.3.9.12 转换规则为:
10 00001010
3 00000011
9 00001001
12 00001100
再将以上二进制拼接起来计算十进制结果:00001010 00000011 00001001 00001100 = ?
print(int(''.join([bin(num).strip('0b').zfill(8) for num in list(map(int, input("Please Input IP:").split('.')))]), 2))
默认的在window上的最大递归层数是998,Python的最大递归层数是可以通过sys.setrecursionlimit()
设置的,但是一般默认不会超过3925-3929这个范围。
v1 = 1 or 3
v2 = 1 and 3
v3 = 0 and 2 and 1
v4 = 0 and 2 or 1
v5 = 0 and 2 or 1 or 4
v6 = 0 or Flase and 1
v1 = 1
v2 = 3
v3 = 0
v4 = 1
v5 = 1
v6 = False
在计算机内部,所有的信息最终都表示为一个二进制的字符串。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从0000000到11111111。
上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为ASCII码,一直沿用至今。
ASCII码一共规定了128个字符的编码,这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的1位统一规定为0。
世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方式不一样。
可以想象,如果有一种编码,将世界上所有的符号都纳入其中。每一个符号都给予一个独一无二的编码,那么乱码问题就会消失。这就是Unicode,就像它的名字都表示的,这是一种所有符号的编码。
Unicode当然是一个很大的集合,现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,U+0639表示阿拉伯字母Ain,U+0041表示英语的大写字母A,U+4E25表示汉字“严”。具体的符号对应表,可以查询unicode.org,或者专门的汉字对应表。
需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。
比如,汉字“严”的unicode是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多。
这里就有两个严重的问题,第一个问题是,如何才能区别unicode和ascii?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。
它们造成的结果是:1)出现了unicode的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示unicode。2)unicode在很长一段时间内无法推广,直到互联网的出现。
互联网的普及,强烈要求出现一种统一的编码方式。UTF-8就是在互联网上使用最广的一种unicode的实现方式。其他实现方式还包括UTF-16和UTF-32,不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8是Unicode的实现方式之一。
UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
由于ASCII编码不支持中文,因此,当中国人用到计算机时,就需要寻求一种编码方式来支持中文。
于是,国人就定义了一套编码规则:当字符小于127位时,与ASCII的字符相同,但当两个大于127的字符连接在一起时,就代表一个汉字,第一个字节称为高字节(从0xA1-0xF7),第二个字节为低字节(从0xA1-0xFE),这样大约可以组合7000多个简体汉字。这个规则叫做GB2312。
但是由于中国汉字很多,有些字无法表示,于是重新定义了规则:不在要求低字节一定是127之后的编码,只要第一个字节是大于127,就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。这种扩展之后的编码方案称之为GBK标,包括了GB2312的所有内容,同时新增了近20000个新的汉字(包括繁体字)和符号。
但是,中国有56个民族,所以,我们再次对编码规则进行了扩展,又加了近几千个少数民族的字符,于是再次扩展后得编码叫做GB18030。中国的程序员觉得这一系列编码的标准是非常的好,于是统统称他们叫做"DBCS"(Double Byte Charecter Set 双字节字符集)。
机器码(machine code),学名机器语言指令,有时也被称为原生码(Native Code),是电脑的CPU可直接解读的数据。
通常意义上来理解的话,机器码就是计算机可以直接执行,并且执行速度最快的代码。
用机器语言编写程序,编程人员要首先熟记所用计算机的全部指令代码和代码的涵义。
手编程序时,程序员得自己处理每条指令和每一数据的存储分配和输入输出,还得记住编程过程中每步所使用的工作单元处在何种状态。这是一件十分繁琐的工作,编写程序花费的时间往往是实际运行时间的几十倍或几百倍。
而且,编出的程序全是些0和1的指令代码,直观性差,还容易出错。现在,除了计算机生产厂家的专业人员外,绝大多数的程序员已经不再去学习机器语言了。
总结:机器码是电脑CPU直接读取运行的机器指令,运行速度最快,但是非常晦涩难懂,也比较难编写,一般从业人员接触不到。
字节码(Bytecode)是一种包含执行程序、由一序列 op 代码/数据对 组成的二进制文件。字节码是一种中间码,它比机器码更抽象,需要直译器转译后才能成为机器码的中间代码。
通常情况下它是已经经过编译,但与特定机器码无关。字节码通常不像源码一样可以让人阅读,而是编码后的数值常量、引用、指令等构成的序列。
字节码主要为了实现特定软件运行和软件环境、与硬件环境无关。字节码的实现方式是通过编译器和虚拟机器。编译器将源码编译成字节码,特定平台上的虚拟机器将字节码转译为可以直接执行的指令。字节码的典型应用为Java bytecode。
字节码在运行时通过JVM(JAVA虚拟机)做一次转换生成机器指令,因此能够更好的跨平台运行。
总结:字节码是一种中间状态(中间码)的二进制代码(文件)。需要直译器转译后才能成为机器码。
三元运算符就是在赋值变量的时候,可以加判断,然后根据条件赋值。
格式:[on_true] if [expression] else [on_false]
res = 值1 if 条件 else 值2
Python 3.0 运行 pystone benchmark 的速度比 Python2.5 慢30%,Guido认为 Python 3.0 有极大的优化空间,在字符串和整形操作上可以取得很好的优化结果。
Python 3.1 性能比 Python 2.5 慢15%,还有很大的提升空间。
Python 2.x 源码文件默认使用ASCII编码,Python 3.x 源码文件默认使用UTF-8编码。
去除print语句,加入print()函数实现相同的功能,同样的还有 exec语句,已经改为exec()函数。
1)Python 3.x 去除了long类型,现在只有一种整型——int,但它的行为就像2.x 版本的long
2)新增了bytes类型,对应于2.x版本的八位串
a = 1
b = 2
a, b = b, a
Python 2 有非浮点数准备的 int 和 long 类型。int类型最大值不能超过sys.maxint,而且这个最大值是平台相关的。可以通过在数字的末尾附上一个L来定义长整型,显然,它比int类型表示的数字范围更大。
在Python 3里,只有一种整数类型int,大多数情况下,和Python 2中的长整型类似。
首先得说明一下,只有在python2中才有xrange和range,python3中没有xrange,并且python3中的range和python2中的range有本质的区别。所以这儿说的range和xrange的区别是只针对python2的。
在py2中,range得到的是一个列表,即
x = range(0, 5)
print(type(x)) # 打印x的类型,结果是list
print(x) # 结果是[0,1,2,3,4]
xrange得到的是一个生成器对象, 即
x = xrange(0, 5)
print(type(x)) # 输出类型,结果为一个生成对象
print(x) # 输出x, 结果为xrange(0,5)
它们的使用都是一样的,比如都可以用for循环遍历所有的值
那么,python3中为什么只有range了呢,这个range其实就是py2中的xrange,而不是range,因为使用生成器可以节约内存。
比如现在有个代码是for i in range(0, 10000),如果还是使用py2中的range的话,那你就会得到一个0到9999的一个列表,这个将会占用你很大的空间,但是使用生成器的话,就会节省很大的资源。
xreadlines返回的是一个生成器,readlines返回的是list。
print("1. ", bool(0))
print("2. ", bool(-0))
print("3. ", bool(None))
print("4. ", bool())
print("5. ", bool(False))
print("6. ", bool([]))
print("7. ", bool(()))
print("8. ", bool({}))
print("9. ", bool(0j))
print("10. ", bool(0.0))
words = "today is a wonderfulday"
print(words.strip('today')) # 如果strip方法指定一个值的话,那么会去掉这两个值
print(words.count('a')) # 统计字符串出现的次数
print(words.index('is')) # 找下标
print(words.index('z')) # 找下标如果元素不找不到的话,会报错
print(words.find('z')) # 找下标,如果元素找不到的话,返回-1
print(words.replace('day', 'DAY')) # 字符串替换
# 末尾追加
a = [1, 2, 3, 4, 5]
a.append(6)
print(a)
# 指定位置的前面插入一个元素
a.insert(2, 100) # 在下标为2的前面插入一个元素100
print(a)
# 扩展列表list.extend(iterable),在一个列表上追加一个列表
a.extend([10, 11, 12])
print(a)
# 从list删除元素
# List.remove() 删除方式1:参数object 如果重复元素,只会删除最靠前的.
a = [1,2,3]
a.remove(2) # 返回值是None
# List.pop() 删除方式2:pop 可选参数index,删除指定位置的元素 默认为最后一个元素
a = [1,2,3,4,5]
a.pop()
print(a)
a.pop(2)
print(a)
# 终极删除,可以删除列表或指定元素或者列表切片,list删除后无法访问
a = [1,2,3,4,5,6]
del a[1]
print(a) # 1, 3, 4, 5, 6]
del a[1:]
print(a) # 1
del a
#元组也是一个list,他和list的区别是元组的元素无法修改
tuple1 = (2, 3, 4, 5, 6, 4, 7)
print(type(tuple1))
print(tuple1[:7])
print(tuple1[: 5: -1])
for i in range(6):
print(tuple1[i])
for i in tuple1:
print(i)
# 对元组进行排序
T = ('c','a','d','b')
tmp = list(T)
tmp.sort() ==> ['a','b','c','d']
T = tunple(tmp)
sorted(T)
D[key] = value # 如果key已经存在则修改,如果不存在就创建.
D.get(key, 0) # 同dict[key],多了个没有则返回缺省值,0。[]没有则抛异常
D.has_key(key) # 有该键返回TRUE,否则FALSE
D.keys() # 返回字典键的列表
D.clear() # 清空字典,同del dict
D.copy() # 拷贝字典
lambda表达式,又叫匿名函数,代替一些简单的函数,使得代码看上去更简洁并且可读性高。
lambda args:expression
expression只能是表达式,不能是语句或者代码块,可以接受多个参数,但是只返回一个值。
好处:
轻便
与map,filter,reduce函数式编程相结合使用
pass语句在函数中的作用
编写一个程序时,执行语句部分思路还没有完成,可以用pass语句来占位,也可以当做是一个标记,是要过后来完成的代码。
pass语句在循环中的作用
pass也常用于为复合语句编写一个空的主体,比如说你想一个while语句的无限循环,每次迭代时不需要任何操作。
*arg会把多出来的位置参数转化为tuple
**kwarg会把关键字参数转化为dict
Python中对象包含的三个基本要素,分别是:id(身份标识)、type(数据类型)和value(值)。
is和==都是对对象进行比较判断作用的,但对对象比较判断的内容并不相同。
==是python标准操作符中的比较操作符,用来比较判断两个对象的value(值)是否相等。
is也被叫做同一性运算符,这个运算符比较判断的是对象间的唯一身份标识,也就是id是否相同。
深浅拷贝用法来自copy模块。
导入模块:import copy
浅拷贝:copy.copy
深拷贝:copy.deepcopy
对于 数字 和 字符串 而言,赋值、浅拷贝和深拷贝无意义,因为其永远指向同一个内存地址。
浅拷贝指仅仅拷贝数据集合的第一层数据,深拷贝指拷贝数据集合的所有层。所以对于只有一层的数据集合来说深浅拷贝的意义是一样的,比如字符串,数字,还有仅仅一层的字典、列表、元祖等。
深拷贝的时候python将所有数据在内存中新建了一份,所以如果你修改新的模版的时候老模版不会变。
引用计数(python默认):记录该对象当前被引用的次数,每当新的引用指向该对象时,它的引用计数ob_ref加1,每当该对象的引用失效时计数ob_ref减1,一旦对象的引用计数为0,该对象立即被回收。
标记清除:基于追踪回收(tracing GC)技术实现的垃圾回收算法,首先会给所有的活动对象打上标记,然后会把那些没有标记的对象进行回收。
分代回收:python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,比如有年轻代、中年代、老年代,年轻代最先被回收。
可变类型:类型的数据经过初始化创建成功后,对应内存地址上的数据可以发生多次局部变化,例如:List,Dict,Set。
不可变类型:类型的数据一旦经过初始化创建成功,对应内存地址上的数据本身便不能发生任何变化,例如:Number,String,Tuple。
v = dict.fromkeys(['k1', 'k2'], [])
v['k1'].append(666)
print(v)
v['k1'] = 777
print(v)
输出结果为:
{'k1': [666], 'k2': [666]}
{'k1': 777, 'k2': [666]}
def num():
return [lambda x: i * x for i in range(4)]
print([m(2) for m in num()])
输出结果为:
[6, 6, 6, 6]
Python内置函数就是python标准库里(语言自身携带的)函数(公共函数)。
1、abs() 此函数返回数字的绝对值。
2、bin() 返回一个整数 int 的二进制表示。
3、chr() 用一个范围在 range(256)内的(就是0~255)整数作参数,返回一个对应的字符。(只能输入数字)
4、dict() 函数用于创建一个字典。
5、enumerate() 将一个可遍历的数据对象组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
6、eval() 函数用来执行一个字符串表达式,并返回表达式的值。
7、float() 函数用于将整数和字符串转换成浮点数。
8、frozenset() 返回一个冻结的集合,冻结后集合不能再添加或删除任何元素。
9、getattr() 函数用于返回一个对象属性值。
10、hash() 用于获取取一个对象(字符串或者数值等)的哈希值。
11、hex() 函数用于将10进制整数转换成16进制整数。
12、id() 函数用于获取对象的内存地址。
13、input() 输入函数
14、int() 函数用于将一个字符串会数字转换为整型。
15、isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。
isinstance() 与 type() 区别:
type() 不会认为子类是一种父类类型,不考虑继承关系。
isinstance() 会认为子类是一种父类类型,考虑继承关系。
如果要判断两个类型是否相同推荐使用 isinstance()。
16、len() 方法返回对象(字符、列表、元组等)长度或项目个数。
17、list() 方法用于将元组转换为列表。
18、locals() 函数会以字典类型返回当前位置的全部局部变量。
19、max() 方法返回给定参数的最大值,参数可以为序列。
20、min() 方法返回给定参数的最小值,参数可以为序列。
21、oct() 函数将一个整数转换成8进制字符串。
22、open() 函数用于打开一个文件,创建一个 file 对象,相关的方法才可以调用它进行读写。
23、ord() 函数与chr()函数相反,输入字符返回数字
24、pow() 方法返回 xy(x的y次方) 的值。函数是计算x的y次方,如果z在存在,则再对结果进行取模,其结果等效于pow(x,y) %z
25、print() 输出函数
26、range() 函数可创建一个整数列表,一般用在 for 循环中。
27、everse() 函数用于反向列表中元素。
28、round() 方法返回浮点数x的四舍五入值。
29、set() 函数创建一个无序不重复元素集,可进行关系测试,删除重复数据,还可以计算交集、差集、并集等。
30、str() 函数将对象转化字符串
31、sum() 方法对系列进行求和计算。
32、tuple() 元组 tuple() 函数将列表转换为元组。
33、type() 返回对象类型。
第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。
用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果,逐步迭代。
print('\n'.join(['\t'.join([f'{row} * {col} = {row * col}' for col in range(1, row + 1)]) for row in range(1, 10)]))
输出结果为:
1 * 1 = 1
2 * 1 = 2 2 * 2 = 4
3 * 1 = 3 3 * 2 = 6 3 * 3 = 9
4 * 1 = 4 4 * 2 = 8 4 * 3 = 12 4 * 4 = 16
5 * 1 = 5 5 * 2 = 10 5 * 3 = 15 5 * 4 = 20 5 * 5 = 25
6 * 1 = 6 6 * 2 = 12 6 * 3 = 18 6 * 4 = 24 6 * 5 = 30 6 * 6 = 36
7 * 1 = 7 7 * 2 = 14 7 * 3 = 21 7 * 4 = 28 7 * 5 = 35 7 * 6 = 42 7 * 7 = 49
8 * 1 = 8 8 * 2 = 16 8 * 3 = 24 8 * 4 = 32 8 * 5 = 40 8 * 6 = 48 8 * 7 = 56 8 * 8 = 64
9 * 1 = 9 9 * 2 = 18 9 * 3 = 27 9 * 4 = 36 9 * 5 = 45 9 * 6 = 54 9 * 7 = 63 9 * 8 = 72 9 * 9 = 81
通过pip
命令安装第三方模块,如果因为网络问题超时的话,可以通过国内镜像源安装。
如果使用的是Anaconda或者MiniConda的话,通过conda
命令安装,同业也可以通过国内镜像源解决网络问题。
用过的第三方模块就比较多了,比如Django、numpy、pandas、matplotlib等等之类的。
os模块,路径
re模块,正则表达式
sys模块,标准输入输出
math模块,数学公式
json模块,字符串与其他数据类型转换
pickle模块,序列化
random模块,生成随机数
time模块,时间模块
request模型,HTTP请求库
match()函数只检测re是不是在string的开始位置匹配,search()会扫描整个string查找匹配。
也就是说match()只有在0位置匹配成功的话才有返回,如果不是开始位置匹配成功的话,match()就返回none。
1、贪婪匹配
总是尝试匹配尽可能多的字符
2、非贪婪匹配
是尝试匹配尽可能少的字符
a. [ i % 2 for i in range(10) ]
b. ( i % 2 for i in range(10) )
输出结果为:
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
at 0x0000025164D46C48>
a. 1 or 2
b. 1 and 2
c. 1 < (2==2)
d. 1 < 2 == 2
输出结果为:
1
2
False
True
函数的第二个默认参数是一个list,当第一次执行的时候实例化了一个list,第二次执行还是用第一次执行的时候实例化的地址存储。
通过程序看看这个函数有什么坑吧
def func(a,b=[]):
b.append(a)
print(b)
func(1)
func(1)
func(1)
func(1)
输出结果为:
[1]
[1, 1]
[1, 1, 1]
[1, 1, 1, 1]
三次执行的结果就是 [1, 1, 1] ,想每次执行只输出[1] ,默认参数应该设置为None。
print('1,2,3'.split(','))
print(list(map(int, ['1', '2', '3'])))
a = [1, 2, 3]
b = [(1), (2), (3)]
c = [(1,), (2,), (3,)]
print(a)
print(type(a[0]))
print(b)
print(type(b[0]))
print(c)
print(type(c[0]))
执行结果为:
[1, 2, 3]
[1, 2, 3]
[(1,), (2,), (3,)]
print([x * x for x in range(1, 11)])
array = ['b', 'c', 'd', 'b', 'c', 'a', 'a']
print(list(set(array)))
在函数的内部,通过global声明,使在函数内部中设置一个全局变量,这个全局变量可以在任意的函数中进行调用!
logging模块定义的函数和类为应用程序和库的开发实现了一个灵活的事件日志系统。
logging模块是Python的一个标准库模块,由标准库模块提供日志记录API的关键好处是所有Python模块都可以使用这个日志记录功能。所以,你的应用日志可以将你自己的日志信息与来自第三方模块的信息整合起来。
logging模块默认定义了以下几个日志等级:
日志等级(level) |
描述 |
---|---|
DEBUG |
最详细的日志信息,典型应用场景是 问题诊断 |
INFO |
信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作 |
WARNING |
当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的 |
ERROR |
由于一个更严重的问题导致某些功能不能正常运行时记录的信息 |
CRITICAL |
当发生严重错误,导致应用程序不能继续运行时记录的信息 |
class Stack:
def __init__(self):
self.stack = []
def push(self, value):
self.stack.append(value)
def pop(self):
if self.stack:
self.stack.pop()
else:
raise LookupError("Stack is Empty!")
def top(self):
return self.stack[-1]
def isEmpty(self):
return bool(self.stack)
迭代器对象就是实现了iter() 和 next()方法的对象.其中iter()返回迭代器本身,而next()返回容器的下一个元素,在结尾处引发StopInteration异常.
生成器(Generator)是创建迭代器的简单而强大的工具。它们写起来就像是正规的函数,只是在需要返回数据的时候使用 yield 语句。
在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。
生成器对延迟操作提供了支持.所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。
可以直接作用于for循环的叫可迭代对象,可迭代对象包括以下2种:
1.list、tuple、set、dict、str等集合数据类型
2.生成器
def binarySearch(array: list, target: int):
left, right = 0, len(array) - 1
while left <= right:
middle = (right + left) // 2
if array[middle] == target:
return middle
if array[middle] < target:
left = middle + 1
else:
right = middle - 1
return -1
关于闭包,即函数定义和函数表达式位于另一个函数的函数体内(嵌套函数)。
而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数。
当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。
也就是说,内部函数会在外部函数返回后被执行。
而当这个内部函数执行时,它仍然必需访问其外部函数的局部变量、参数以及其它内部函数。
这些局部变量、参数和函数声明(最初时)的值时外部函数返回时的值,但也会受到内部函数的影响。
闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外包裹了一层作用域,这使得该函数无论在何处调用,优先使用自己外层包裹的作用域。
os:这个模块提供了一种方便的使用操作系统函数的方法。
sys:这个模块可供访问由解释器使用或维护的变量和与解释器进行交互的函数。
总结:os模块负责程序与操作系统的交互,提供了访问操作系统底层的接口;sys模块负责程序与python解释器的交互,提供了一系列的函数和变量,用于操控python的运行时环境。
在 Python 中用于生成随机数的模块是 random,在使用前需要 import。
random.random():生成一个 0-1 之间的随机浮点数;
random.uniform(a, b):生成[a,b]之间的浮点数;
random.randint(a, b):生成[a,b]之间的整数;
random.randrange(a, b, step):在指定的集合[a,b)中,以 step 为基数随机取一个数;
random.choice(sequence):从特定序列中随机取一个元素,这里的序列可以是字符串,列表,元组等。
若想利用python删除windows里的文件,这里需要使用os模块!
os.remove(path):删除文件 path。 如果path是一个目录, 抛出 OSError错误。
os.removedirs(path):递归地删除目录。类似于rmdir(), 如果子目录被成功删除, removedirs() 将会删除父目录;但子目录没有成功删除,将抛出错误。
os.rmdir(path):删除目录 path,要求path必须是个空目录,否则抛出OSError错误。
面向对象是向现实世界模型的自然延伸,这是一种“万物皆对象”的编程思想。在现实生活中的任何物体都可以归为一类事物,而每一个个体都是一类事物的实例。面向对象的编程是以对象为中心,以消息为驱动,所以程序=对象+消息。
面向对象有三大特性,封装、继承和多态。
封装就是将一类事物的属性和行为抽象成一个类,使其属性私有化,行为公开化,提高了数据的隐秘性的同时,使代码模块化。这样做使得代码的复用性更高。
继承则是进一步将一类事物共有的属性和行为抽象成一个父类,而每一个子类是一个特殊的父类–有父类的行为和属性,也有自己特有的行为和属性。这样做扩展了已存在的代码块,进一步提高了代码的复用性。
如果说封装和继承是为了使代码重用,那么多态则是为了实现接口重用。多态的一大作用就是为了解耦–为了解除父子类继承的耦合度。如果说继承中父子类的关系式IS-A的关系,那么接口和实现类之之间的关系式HAS-A。简单来说,多态就是允许父类引用(或接口)指向子类(或实现类)对象。很多的设计模式都是基于面向对象的多态性设计的。
总结一下,如果说封装和继承是面向对象的基础,那么多态则是面向对象最精髓的理论。掌握多态必先了解接口,只有充分理解接口才能更好的应用多态。
减少代码和灵活制定新类
子类具有父类的属性和方法
子类不能继承父类的私有属性/方法
子类可以添加新的方法
子类可以修改父类的方法
1、在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。
2、在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别于在类中调用普通函数时并不需要带上self参数。
3、Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。
1、经典类
当类是经典类时,多继承情况下,在要查找属性不存在时,会按照深度优先的方式查找下去。
在python2中,没有继承object的类,以及它的子类都称之为经典类
2、新式类
当类是新式类时,多继承情况下,在要查找属性不存在时,会按照广度优先的方式查找下去。
在python3中,统一都是新式类。
在python2中,继承object的类,以及它的子类都称之为新式类。
super() 函数是用于调用父类(超类)的一个方法。
super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
functools用于高阶函数:指那些作用于函数或者返回其他函数的函数。通常情况下,只要是可以被当做函数调用的对象就是这个模块的目标。
functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
__new__
负责对象的创建而 __init__
负责对象的初始化。
__new__
:创建对象时调用,会返回当前对象的一个实例
__init__
:创建完对象后调用,对当前对象的一些实例初始化,无返回值
__del__
:析构方法,当对象在内存中被释放时,自动触发执行。
__str__
:改变对象的字符串显示
__doc__
:类的描述信息,该描述信息无法被继承
__ecter__
和__exit__
:出现with语句,对象的__ecter__
被触发,有返回值则赋值给as声明的变量;with中代码块执行完毕时执行__exit__
里边的内容。
通过类方法调用为函数,通过实例化对象调用为方法。
可以用内置的isinstance 来判断。
类方法是这个类可以调用的方法,那么参数需要把这个类本身传进去,不需要实例化就可以使用。
静态方法是类中的一个普通函数或者说方法,参数和普通的传参一样,类或者实例化的对象都可以直接使用它,也就是说这个静态方法也不需要实例化就可以调用。
__doc__
描述类的信息__call__
对象后面加括号,触发执行__dict__
查看类或对象中的所有成员__str__
如果一个类中定义了__str__
方法,那么在打印对象时,默认输出该方法的返回值__getitem__
、 __setitem__
、__delitem__
用于索引操作,如字典。分别表示获取、设置、删除数据numberList = [1, 2, 3, 4, 5]
complexList = []
def permutationNum():
for i in numberList:
for j in numberList:
for k in numberList:
if i != j and k != j and i != k:
complexList.append(str(i) + str(j) + str(k))
print(f"共有{len(complexList)}种组合,分别为{complexList}")
permutationNum()
输出结果为:
共有60种组合,分别为['123', '124', '125', '132', '134', '135', '142', '143', '145', '152', '153', '154', '213', '214', '215', '231', '234', '235', '241', '243', '245', '251', '253', '254', '312', '314', '315', '321', '324', '325', '341', '342', '345', '351', '352', '354', '412', '413', '415', '421', '423', '425', '431', '432', '435', '451', '452', '453', '512', '513', '514', '521', '523', '524', '531', '532', '534', '541', '542', '543']
python面向对象中的反射:通过字符串的形式操作对象相关的属性。
有的时候,程序不一定要按照预置好顺序执行,而是要按照用户输入的指令选择执行相应的函数,但是用户输入的都是字符串,就需要一种通过字符串来操作对象的方法。
metaclass用来指定类是由谁创建的。
类的metaclass 默认是type。我们也可以指定类的metaclass值。
一、模块单例
Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。
二、静态变量方法
先执行了类的__new__方法(我们没写时,默认调用object.__new__),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所有我们可以基于这个,实现单例模式。
三、元类方法
此方法是在__new__方法的更上层对实例化过程进行控制。
原理:执行元类的 元类的__new__
方法和__init__
方法用来实例化类对象,__call__
方法用来对实例化的对象的实例即类的对象进行控制。__call__
方法会调用实例类的__new__
方法,用于创建对象。返回对象给__call__
方法,然后调用类对象的__init__
方法,用于对对象初始化。
四、装饰器
原理:装饰器用来控制类调用__call__
方法。
1、授权(Authorization)
装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。这里是一个例子来使用基于装饰器的授权:
from functools import wraps # 最新版python引用是 import functools
def requires_auth(f): # f 就是我们需要装饰的函数,一看就是不带参数的装饰器
@wraps(f) # 新版python写法 @functools.wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
authenticate()
return f(*args, **kwargs)
return decorated # 该装饰器需相关配置才能运行,这里是截取代码展示应用
2.、日志(Logging)
日志是装饰器运用的另一个亮点。这是个例子:
from functools import wraps
def logit(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logit
def addition_func(x):
"""Do some math."""
return x + x
result = addition_func(4)
3.、带参数的装饰器
带参数的装饰器是典型的闭包函数
4.、在函数中嵌入装饰器
我们回到日志的例子,并创建一个包裹函数,能让我们指定一个用于输出的日志文件
from functools import wraps
def logit(logfile='out.log'):
def logging_decorator(func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile,并写入内容
with open(logfile, 'a') as opened_file:
# 现在将日志打到指定的logfile
opened_file.write(log_string + '\n')
return func(*args, **kwargs)
return wrapped_function
return logging_decorator
@logit()
def myfunc1():
pass
myfunc1()
# Output: myfunc1 was called
# 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串
@logit(logfile='func2.log')
def myfunc2():
pass
myfunc2()
# Output: myfunc2 was called
# 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串
异常处理
try:
******
except ValueError as Argument:
******
主动抛出异常
raise [Exception [, args [, traceback]]]
对于定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。
为了实现继承,python会再MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
MRO列表的构造是通过一个C3线性化算法来实现的。
合并所有父类的MRO列表遵循如下三条准则:
isinstance作用:来判断一个对象是否是一个已知的类型;
其第一个参数(object)为对象,第二个参数为类型名(int…)或类型名的一个列表((int,list,float)是一个列表)。其返回值为布尔型(True or flase)。
若对象的类型与参数二的类型相同则返回True。若参数二为一个元组,则若对象类型与元组中类型名之一相同即返回True。
自定义时间序列化转换器
import json
from json import JSONEncoder
from datetime import datetime
class ComplexEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
else:
return super(ComplexEncoder,self).default(obj)
d = { 'name':'alex','data':datetime.now()}
print(json.dumps(d,cls=ComplexEncoder))
# {"name": "alex", "data": "2018-05-18 19:52:05"}
在序列化时,中文汉字总是被转换为unicode码,在dumps函数中添加参数ensure_ascii=False即可解决。
Python的assert是用来检查一个条件,如果它为真,就不做任何事。如果它为假,则会抛出AssertError并且包含错误信息。
断言应该用于:
1.防御型的编程
2.运行时检查程序逻辑
3.检查约定
4.程序常量
5.检查文档
with语句使用所谓的上下文管理器对代码块进行包装,允许上下文管理器实现一些设置和清理操作。
例如:文件可以作为上下文管理器使用,它们可以关闭自身作为清理的一部分。
1:使用os.listdir
2:使用glob模块,可以设置文件过滤
3:通过os.path.walk递归遍历,可以访问子文件夹
yield
当一个函数中出现yield关键字的时候,那么这个函数就是一个生成器。可以用for循环或者next()函数来迭代。yield 可以接收到外界传入的变量。
yield from
事实上,yield from iterable就是for item in iterable: yield item的语法糖。注意yield from 后面一定是iterable。