python面试题库——1Python基础篇(80题)

时间:2018-06-25 01:23:59   收藏:0   阅读:3658

 

第一部分 Python基础篇(80题)

 

 



 

 

  1. b、B、KB、MB、GB 的关系?

1024B=1K(千)B 
1024KB=1M(兆)B 
1024MB=1G(吉)B 
1024GB=1T(太)B 

   请至少列举5个 PEP8 规范(越多越好)。

  1. 缩进。4个空格的缩进(编辑器都可以完成此功能),不使用Tap,更不能混合使用Tap和空格。
  2. 每行最大长度79,换行可以使用反斜杠,最好使用圆括号。换行点要在操作符的后边敲回车。
  3. 类和top-level函数定义之间空两行;类中的方法定义之间空一行;函数内逻辑无关段落之间空一行;其他地方尽量不要再空行。
  4. 块注释,在一段代码前增加的注释。在‘#’后加一空格。段落之间以只有‘#’的行间隔
  • 各种右括号前不要加空格。
  • 逗号、冒号、分号前不要加空格。
  • 函数的左括号前不要加空格。如Func(1)。
  • 序列的左括号前不要加空格。如list[2]。
  • 操作符左右各加一个空格,不要为了对齐增加空格。
  • 函数默认参数使用的赋值符左右省略空格。
  • 不要将多句语句写在同一行,尽管使用‘;’允许。
  • if/for/while语句中,即使执行语句只有一句,也必须另起一行。
  • 类的方法第一个参数必须是self,而静态方法第一个参数必须是cls。
  1. 通过代码实现如下转换:

    二进制转换成十进制:v = “0b1111011”?
    十进制转换成二进制:v = 18? 
    八进制转换成十进制:v = “011”? 
    十进制转换成八进制:v = 30? 
    十六进制转换成十进制:v = “0x12”? 
    十进制转换成十六进制:v = 87

(1)二进制转换成十进制:v = “0b1111011”?

#先将其转换为字符串,再使用int函数,指定进制转换为十进制。
print(int("0b1111011",2))
值为123

(2)十进制转换成二进制:v = 18? 

print("转换为二进制为:", bin(18))
#转换为二进制为: 0b10010

(3)八进制转换成十进制:v = “011”? 

print(int("011",8))
#9

(4)十进制转换成八进制:v = 30

print("转换为八进制为:", oct(30))
#转换为八进制为: 0o36

(5)十六进制转换成十进制:v = “0x12”? 

print(int("0x12",16))
#18

(6)十进制转换成十六进制:v = 87

print("转换为十六进制为:", hex(87))
转换为十六进制为: 0x57

 

 

 



 

 

  1. b、B、KB、MB、GB 的关系?

1024B=1K(千)B 
1024KB=1M(兆)B 
1024MB=1G(吉)B 
1024GB=1T(太)B 

   请至少列举5个 PEP8 规范(越多越好)。

  1. 缩进。4个空格的缩进(编辑器都可以完成此功能),不使用Tap,更不能混合使用Tap和空格。
  2. 每行最大长度79,换行可以使用反斜杠,最好使用圆括号。换行点要在操作符的后边敲回车。
  3. 类和top-level函数定义之间空两行;类中的方法定义之间空一行;函数内逻辑无关段落之间空一行;其他地方尽量不要再空行。
  4. 块注释,在一段代码前增加的注释。在‘#’后加一空格。段落之间以只有‘#’的行间隔
  • 各种右括号前不要加空格。
  • 逗号、冒号、分号前不要加空格。
  • 函数的左括号前不要加空格。如Func(1)。
  • 序列的左括号前不要加空格。如list[2]。
  • 操作符左右各加一个空格,不要为了对齐增加空格。
  • 函数默认参数使用的赋值符左右省略空格。
  • 不要将多句语句写在同一行,尽管使用‘;’允许。
  • if/for/while语句中,即使执行语句只有一句,也必须另起一行。
  • 类的方法第一个参数必须是self,而静态方法第一个参数必须是cls。
  1. 通过代码实现如下转换:

    二进制转换成十进制:v = “0b1111011”?
    十进制转换成二进制:v = 18? 
    八进制转换成十进制:v = “011”? 
    十进制转换成八进制:v = 30? 
    十六进制转换成十进制:v = “0x12”? 
    十进制转换成十六进制:v = 87

(1)二进制转换成十进制:v = “0b1111011”?

#先将其转换为字符串,再使用int函数,指定进制转换为十进制。
print(int("0b1111011",2))
值为123

(2)十进制转换成二进制:v = 18? 

print("转换为二进制为:", bin(18))
#转换为二进制为: 0b10010

(3)八进制转换成十进制:v = “011”? 

print(int("011",8))
#9

(4)十进制转换成八进制:v = 30

print("转换为八进制为:", oct(30))
#转换为八进制为: 0o36

(5)十六进制转换成十进制:v = “0x12”? 

print(int("0x12",16))
#18

(6)十进制转换成十六进制:v = 87

print("转换为十六进制为:", hex(87))
转换为十六进制为: 0x57

 

 如何实现[‘1’,’2’,’3’]变成[1,2,3] ?

a=[‘1‘,‘2‘,‘3‘]
b=[int(i) for i in a]
print(b)

输出为:[1, 2, 3]

 

 

def fab(n):
if n == 1:
return 1
else:
return fab(n-1)+ n

print (fab(998))


#得到的最大数为998,以后就是报错了,998这个数值莫名想起广告词····
技术分享图片

 

技术分享图片
import sys
sys.setrecursionlimit(100000)

def foo(n):
    print(n)
    n += 1
    foo(n)
        
if __name__ == ‘__main__‘:
    foo(1)

#得到的最大数字在3922-3929之间浮动,这个是和计算机有关系的,将数字调到足够大了,已经大于系统堆栈,python已经无法支撑到太大的递归崩了。

 

 v1 = 1 or 3 #1

v2 = 1 and 3  #3
v3 = 0 and 2 and 1  #0
v4 = 0 and 2 or 1  #1
v5 = 0 and 2 or 1 or 4  #1
v6 = 0 or False and 1  #False

 

 

 

 

 ASCII码使用一个字节编码,所以它的范围基本是只有英文字母、数字和一些特殊符号 ,只有256个字符。

 

  在表示一个Unicode的字符时,通常会用“U+”然后紧接着一组十六进制的数字来表示这一个字符。在基本多文种平面(英文为 Basic Multilingual Plane,简写 BMP。它又简称为“零号平面”, plane 0)里的所有字符,要用四位十六进制数(例如U+4AE0,共支持六万多个字符);在零号平面以外的字符则需要使用五位或六位十六进制数了。旧版的Unicode标准使用相近的标记方法,但却有些微的差异:在Unicode 3.0里使用“U-”然后紧接着八位数,而“U+”则必须随后紧接着四位数。
Unicode能够表示全世界所有的字节
  GBK是只用来编码汉字的,GBK全称《汉字内码扩展规范》,使用双字节编码。
  UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,又称万国码。由Ken Thompson于1992年创建。现在已经标准化为RFC 3629。UTF-8用1到6个字节编码UNICODE字符。用在网页上可以同一页面显示中文简体繁体及其它语言(如英文,日文,韩文)。

 

 

字节码

 

字节码是一种中间码
字节码通常指的是已经经过编译,但与特定机器码无关,需要直译器转译后才能成为机器码的中间代码。字节码通常不像源码一样可以让人阅读,而是编码后的数值常量、引用、指令等构成的序列。
字节码主要为了实现特定软件运行和软件环境、硬件环境无关。字节码的实现方式是通过编译器和虚拟机器。编译器将源码编译成字节码,特定平台上的虚拟机器将字节码转译为可以直接执行的指令。字节码的典型应用为Java语言。
总结:字节码是一种中间状态(中间码)的二进制代码(文件)。需要直译器转译后才能成为机器码。

 

机器码

 

机器码就是计算机可以直接执行,并且执行速度最快的代码。

 

用机器语言编写程序,编程人员要首先熟记所用计算机的全部指令代码和代码的涵义。手编程序时,程序员得自己处理每条指令和每一数据的存储分配和输入输出,还得记住编程过程中每步所使用的工作单元处在何种状态。这是一件十分繁琐的工作,编写程序花费的时间往往是实际运行时间的几十倍或几百倍。而且,编出的程序全是些0和1的指令代码。
机器语言是微处理器理解和使用的,用于控制它的操作二进制代码。
  8086到Pentium的机器语言指令长度可以从1字节到13字节。
  尽管机器语言好像是很复杂的,然而它是有规律的。
  存在着多至100000种机器语言的指令。这意味着不能把这些种类全部列出来。
总结:机器码是电脑CPU直接读取运行的机器指令,运行速度最快

 

 

三元运算符的功能与“if...else”流程语句一致,它在一行中书写,代码非常精练、执行效率更高。在PHP程序中恰当地使用三元运算符能够令脚本更为简洁、高效。代码格式如下:

 

(expr1) ? (expr2) : (expr3);

 

解释:如果条件“expr1”成立,则执行语句“expr2”,否则执行“expr3”。
实现同样的功能,若使用条件流程语句,就需要书写多行代码:

 

if(expr1) {
    expr2;
} else {
    expr3;
}

 

可见,前面所说的三元运算符之好并非夸张。可是,多数情况下我们只在代码较为简单的时候使用三元运算符,即执行语句只为单句的时候。如:

 

$a>$b ? print "a大于b" : print "a小于b";

 

事实上,三元运算符可以扩展使用,当设置的条件成立或不成立,执行语句都可以不止一句,试看以下格式:

 

(expr1) ? (expr2).(expr3) : (expr4).(expr5);

 

我们非常明显地看到,多个执行语句可以使用用字符串运算符号(“.”)连接起来,各执行语句用小角括号包围起来以表明它是一个独立而完整的执行语句。这样扩展后它的功能更为逼近“if...else”流程语句。

 

同时三元运算符还可以嵌套使用。例如,a大于b成立时:如果a小于c,那么x=c-a否则x=a-c;否则a小于b成立时:如果b小于c,那么x=c-b否则x=b-c:

 

$a>$b ? $x=($a<$c ? $c-$a : $a-$c) : $x=($b<$c ? $c-$b : $b-$c);

 

嵌套使用的三元运算符可读性不太好,日后对代码的维护极可能存在问题,但比起“if...else”之类的流程语句,在上述情形之下,它的确太简练了,这是它的诱人之处。

 

对于喜欢偷懒和追求代码简洁的人来说,用三元运算符取代if流程语句应该是绝佳的选择。即使不用考虑“三元”中条件句以外的任意某一“元”,使用三元运算符仍然比if语句简练。以下语句的语法是正确的,它们以小解引号的方式忽略了第二或第三“元”:

 

$a>$b ? print "Yes" : "";
$a>$b ? ‘‘: print ‘No‘;

 

应该注意的是:在使用三元运算符时,建议使用print语句替代echo语句。

 

Python自省

这个也是python彪悍的特性.

自省就是面向对象的语言所写的程序在运行时,所能知道对象的类型.简单一句就是运行时能够获得对象的类型.比如type(),dir(),getattr(),hasattr(),isinstance().

 

(1)Print

 

在 Python 2 中, print 被视为一个语句而不是一个函数,这是一个典型的容易弄混的地方,因为在 Python 中的许多操作都需要括号内的参数来执行。如果在 Python 2 中你想要你的控制台输出 ”Sammy the Shark is my favorite sea creature”,你应该写下这样的 print 语句:

 

print "Sammy the Shark is my favorite sea creature"

 

在使用 Python 3 时,print()会被显式地视为一个函数,因此要输出上面相同的字符串,你可以使用这种非常简单且易于使用的函数语法:

 

print("Sammy the Shark is my favorite sea creature")

 

这种改变使得 Python 的语法更加一致,并且在不同的 print 函数之间进行切换更加容易。就方便性而言,print()语法也与 Python 2.7 向后兼容,因此您的 Python 3 print()函数可以在任一版本中运行。

 

(2)整数的除法

 

在 Python 2 中,您键入的任何不带小数的数字,将被视为整数的编程类型。虽然乍看起来这似乎是一个简单的处理编程类型的方法,但有时候当你试图除以整数以期望获得一个带小数位的答案(称为浮点数),如:

 

5 / 2 = 2.5

 

然而,在 Python 2 中,整数是强类型的,并且不会变成带小数位的浮点数,即使这样做具有直观上的意义。

 

当除法/符号的任一侧的两个数字是整数时,Python 2进行底除法,使得对于商x,返回的数字是小于或等于x的最大整数。这意味着当你写下 5 / 2 来对这两个数字相除时,Python 2.7 将返回最大的小于或等于 2.5 的整数,在这种情形下:

 

a = 5 / 2
print a
#a=2

 

为解决这个问题,你可以在 5.0 / 2.0 中添加小数位,以得到预期的答案 2.5。

 

在 Python 3 中,整数除法变得更直观,如

 

a = 5 / 2
print(a)
#a=2.5

 

你也可以使用 5.0 / 2.0 返回 2.5,但是如果你想做底层划分,你应该使用 “//” 这样的 Python 3 语法,像这样:

 

b = 5 // 2
print(b)
#b=2

 

在 Python 3 中的这种修改使得整数除法更为直观,并且它的特点是不能向后兼容 Python 2.7。

 

(3)支持 Unicode

 

当编程语言处理字符串类型时,也就是一个字符序列,它们可以用几种不同的方式来做,以便计算机将数字转换为字母和其他符号。

 

Python 2 默认使用 ASCII 字母表,因此当您输入“Hello,Sammy!”时, Python 2 将以 ASCII 格式处理字符串。被限定为在多种扩展形式上的数百个字符,用ASCII 进行字符编码并不是一种非常灵活的方法,特别是使用非英语字符时。

 

要使用更通用和更强大的Unicode字符编码,这种编码支持超过128,000个跨越现今和历史的脚本和符号集的字符,你必须输入

 

u“Hello,Sammy!”

 

, 前缀 u 代表 Unicode。

 

Python 3 默认使用 Unicode,这节省了程序员多余的开发时间,并且您可以轻松地在程序中直接键入和显示更多的字符。因为 Unicode 支持更强大的语言字符多样性以及 emoji 的显示,所以将它作为默认字符编码来使用,能确保全球的移动设备在您的开发项目中都能得到支持。

 

如果你希望你的 Python 3 代码向后兼容 Python 2,你可以通过在你的字符串的前面保留 “u” 来实现。

 

(4)后续发展

 

Python 3 和 Python 2 之间的最大区别不是语法上的,而是事实上 Python 2.7 将在 2020 年失去后续的支持,Python 3 将继续开发更多的功能和修复更多的错误。

 

最近的发展包括格式化的字符串,类创建的简单定制,和用一种更干净的句法方式来处理矩阵乘法。

 

Python 3 的后续开发意味着,开发人员可以对问题被及时解决抱有信心,并且随着时间的推移更多的功能将被添加进来,程序也会变得更加有效。

 

 

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

 

long整数类型被Python3废弃,统一使用int

 

python2和python3区别?列举5个

 

1、Python3 使用 print 必须要以小括号包裹打印内容,比如 print(‘hi‘)

 

Python2 既可以使用带小括号的方式,也可以使用一个空格来分隔打印内容,比如 print ‘hi‘

 

2、python2 range(1,10)返回列表,python3中返回迭代器,节约内存

 

3、python2中使用ascii编码,python中使用utf-8编码

 

4、python2中unicode表示字符串序列,str表示字节序列

 

      python3中str表示字符串序列,byte表示字节序列

 

5、python2中为正常显示中文,引入coding声明,python3中不需要

 

6、python2中是raw_input()函数,python3中是input()函数

 

 

range 函数说明:range([start,] stop[, step]),根据start与stop指定的范围以及step设定的步长,生成一个序列。

 

range示例:

 

技术分享图片
>>> range(5)
[0, 1, 2, 3, 4]
>>> range(1,5)
[1, 2, 3, 4]
>>> range(0,6,2)
[0, 2, 4]
技术分享图片

 

xrange 函数说明:用法与range完全相同,所不同的是生成的不是一个数组,而是一个生成器。
xrange示例:

 

技术分享图片
>>> xrange(5)
xrange(5)
>>> list(xrange(5))
[0, 1, 2, 3, 4]
>>> xrange(1,5)  
xrange(1, 5)
>>> list(xrange(1,5))
[1, 2, 3, 4]
>>> xrange(0,6,2)
xrange(0, 6, 2)
>>> list(xrange(0,6,2))
[0, 2, 4]
技术分享图片

 

由上面的示例可以知道:要生成很大的数字序列的时候,用xrange会比range性能优很多,因为不需要一上来就开辟一块很大的内存空间,这两个基本上都是在循环的时候用:

 

for i in range(0, 100):
 print i
 
for i in xrange(0, 100):
 print i

 

这两个输出的结果都是一样的,实际上有很多不同,range会直接生成一个list对象:

 

a = range(0,100)
print type(a)
print a
print a[0], a[1]

 

输出结果:

 

技术分享图片
<type ‘list‘>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
0 1
技术分享图片

 

而xrange则不会直接生成一个list,而是每次调用返回其中的一个值:

 

a = xrange(0,100)
print type(a)
print a
print a[0], a[1]

 

结果如下:

 

<type ‘xrange‘>
xrange(100)
0

 

总结:

 

  1.range和xrange都是在循环中使用,输出结果一样。

 

  2.range返回的是一个list对象,而xrange返回的是一个生成器对象(xrange object)。

 

  3.xrange则不会直接生成一个list,而是每次调用返回其中的一个值,内存空间使用极少,因而性能非常好。

 

注意:Python 3.x已经去掉xrange,全部用range代替。

 

二者使用时相同,但返回类型不同,xreadlines返回的是一个生成器,readlines返回的是list

 

 

 

字符串

 

技术分享图片
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‘))#字符串替换
print(words.isdigit())#判断字符串是否为纯数字
print(words.islower())#判断字符串是否为小写字母
print(words.isupper())#判断字符串是否为大写字母
print(words.startswith(‘http‘))#判断是否以某个字符串开头
print(words.endswith(‘.jpg‘))#判断是否以某个字符串结尾
print(words.upper())#将字符串变成大写
print(words.lower())#将字符串变成小写
技术分享图片

 

列表

 

技术分享图片
sample_list = [‘a‘,1,(‘a‘,‘b‘)]  #创建列表
sample_list = [‘a‘,‘b‘,0,1,3]   #  Python 列表操作
value_start = sample_list[0]  #得到列表中的某一个值
end_value = sample_list[-1]  #得到列表中的某一个值
del sample_list[0]  #删除列表的第一个值
sample_list[0:0] = [‘sample value‘]  #在列表中插入一个值
list_length = len(sample_list)  #得到列表的长度
for element in sample_list:  #列表遍历
    print(element)
技术分享图片

 

元祖

 

技术分享图片
#元组也是一个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)
技术分享图片

 

字典

 

技术分享图片
dict = {‘ob1‘:‘computer‘, ‘ob2‘:‘mouse‘, ‘ob3‘:‘printer‘}
每一个元素是pair,包含key、value两部分。key是Integer或string类型,value 是任意类型。
键是唯一的,字典只认最后一个赋的键值。

dictionary的方法
D.get(key, 0)       #同dict[key],多了个没有则返回缺省值,0。[]没有则抛异常
D.has_key(key)      #有该键返回TRUE,否则FALSE
D.keys()            #返回字典键的列表
D.values()
D.items()

D.update(dict2)     #增加合并字典
D.popitem()         #得到一个pair,并从字典中删除它。已空则抛异常
D.clear()           #清空字典,同del dict
D.copy()            #拷贝字典
D.cmp(dict1,dict2)  #比较字典,(优先级为元素个数、键大小、键值大小)
                    #第一个大返回1,小返回-1,一样返回0
            
dictionary的复制
dict1 = dict        #别名
dict2=dict.copy()   #克隆,即另一个拷贝。

 

 

lambda表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数。

 

lambda所表示的匿名函数的内容应该是很简单的,如果复杂的话,干脆就重新定义一个函数了,使用lambda就有点过于执拗了。

 

lambda就是用来定义一个匿名函数的,如果还要给他绑定一个名字的话,就会显得有点画蛇添足,通常是直接使用lambda函数。如下所示:

 

add = lambda x, y : x+y
print(add(1,2))  # 结果为3

 

那么到底要如何使用lambda表达式呢?

 

1、应用在函数式编程中

 

Python提供了很多函数式编程的特性,如:map、reduce、filter、sorted等这些函数都支持函数作为参数,lambda函数就可以应用在函数式编程中。如下:

 

# 需求:将列表中的元素按照绝对值大小进行升序排列
list1 = [3,5,-4,-1,0,-2,-6]
print(sorted(list1, key=lambda x: abs(x)))  #[0, -1, -2, 3, -4, 5, -6]

 

当然,也可以如下:

 

list1 = [3,5,-4,-1,0,-2,-6]
def get_abs(x):
    return abs(x)
print(sorted(list1,key=get_abs))  # [0, -1, -2, 3, -4, 5, -6]

 

只不过这种方式的代码看起来不够Pythonic

 

2、应用在闭包中

 

def get_y(a,b):
     return lambda x:a*x+b
y1 = get_y(1,1)
print(y1(1)) # 结果为2

 

当然,也可以用常规函数实现闭包,如下:

 

技术分享图片
def get_y(a,b):
    def func(x):
        return a*x+b
    return func
y1 = get_y(1,1)
print(y1(1)) # 结果为2
技术分享图片

 

只不过这种方式显得有点啰嗦。

 

那么是不是任何情况下lambda函数都要比常规函数更清晰明了呢?

 

肯定不是。

 

Python之禅中有这么一句话:Explicit is better than implicit(明了胜于晦涩),就是说那种方式更清晰就用哪一种方式,不要盲目的都使用lambda表达式。

 

 

 1、空语句 do nothing

 

if true:
    pass #do nothing
else:
    pass #do something

 

  2、保证格式完整

 

def iplaypython():
    pass

 

  3、保证语义完整

 

while True:
    pass

 

 

要想理解*arg和**kwarg的作用,先别着急,通过下面的示例,慢慢思考体会下他们的作用是什么?

 

*arg

 

比如现在我有一个最简单的加法(Jiafa)函数:

 

def Jiafa(x, y):
    z = x + y
    return z
print(Jiafa(1,2))

 

这个很简单,一看就知道输出等于3。

 

那下一个问题是,如果我要算不固定个数的数字相加,那怎么来计算呢?

 

这时,就使用args和*kwarg,就可以帮助我们解决这种问题。

 

  *args:可以理解为只有一列的表格,长度不固定。

 

  **kwargs:可以理解为字典,长度也不固定。

 

首先,args和kwarg不是必须成对出现,也不是必须叫这个名字,也可以叫*x和**y。写成这样,只是一种约定俗成,比如给别人讲故事:从前有个张三和李四……大家一听就知道你要说什么了,而不能说从前有个马七狗八,大家虽然也能听懂,但总是不太好理解。

 

先说args的作用,还是开篇的案例,我们要算不定长的加法,就可以用args来定义了,当然也可以叫x,y。

 

技术分享图片
def Jiafa(*args):
    sum = 0
    for i in args:
        sum = sum + i
        print(sum)

Jiafa(1, 3, 5)
Jiafa(2, 4, 6, 8, )        
技术分享图片

 

输出结果,9和20。这个案例很简单,用*args定义就可以引入,相当于定义了不定长度的函数,然后在程序中就可以多次使用。

 

**kwargs

 

**kwargs的字典呢?先看一下普通的字典,用一对大括号{}就可以创建字典,比如下面3行程序,就可以编一个字典的程序:

 

dict = {"system": "系统", "China": "中国", "link": "联接"}

x = input("请输入一个英文单词:")
print(dict.get(x, "本字典里没找到!"))

 

如果输入正确,会得到答案,否则会显示没找到。

 

在这个程序里,dict = {"system": "系统", "China": "中国", "link": "联接"}创建了三对“键和值”(key和value),比如“system”是key,“系统”是key对应的值,也叫键值。

 

还可以写一个测试单词的小软件。

 

dict = {"system": "系统", "China": "中国", "link": "联接"}

 

通过Key找value的语句:

 

y = input("请输入China的中文意思:")
if dict[‘China‘] == y:
    print("恭喜你,答对了!")

 

 通过value找Key的语句:

 

z = input("请输入“系统”的英文单词:")
if list(dict.keys())[list(dict.values()).index("系统")] == z:
    print("恭喜你,答对了!")

 

现在问题来了,如果开始不知道字典里都有哪些内容,需要在程序运程中才知道怎么办?这个时候就可以用kwargs来定义了。我们先用kwargs来定义一个函数,函数内容先都不用写,再看看下面的小程序:

  

技术分享图片
def dict(**kwargs):
return kwargs

mydict = dict(system="系统", China="中国", link="联接")
x = input("请输入单词:")
if x in mydict.keys():
    print("中文意思:", mydict[x])
else:
    print("抱歉,没找到。")
技术分享图片

 

用字典也可以达成这样的功能,使用in……keys(),就是判断这个key是不是存在,如果存在就返回它的值。 同样,用**kwargs传递数据的功能,还可以设计一个用户登录的程序:

 

技术分享图片
def dict(**kwargs):
return kwargs

userdict = dict(user1="1234", user2="5678")

x = input("请输入用户名:")
if x in userdict.keys():
    y = input("请输入密码:")
    if userdict[x] == y:
        print("完全正确")
    else:
        print("密码错误!")
else:
    print("用户不存在!")    
技术分享图片

 

所以从以上的示例可以看到*arg和**kwarg的作用为:

 

  1、函数调用里的*arg和**kwarg

 

    (1)*arg:元组或列表“出现”
       **kwarg:字典“出没”

 

    (2)分割参数

 

  2、函数定义时传的*arg /**kwarg:

 

    (1)接收参数

 

 

 

 

is是对比地址,==是对比值

is和==都是对对象进行比较判断作用的,但对对象比较判断的内容并不相同。下面来看看具体区别在哪。

 ==比较操作符和is同一性运算符区别:

 ==是python标准操作符中的比较操作符,用来比较判断两个对象的value(值)是否相等,例如下面两个字符串间的比较

例1:

>>> a = ‘cheesezh‘
>>> b = ‘cheesezh‘
>>> a == b
True

is也被叫做同一性运算符,这个运算符比较判断的是对象间的唯一身份标识,也就是id是否相同。通过对下面几个list间的比较,你就会明白is同一性运算符的工作原理:

 例2:

技术分享图片
>>> x = y = [4,5,6]
>>> z = [4,5,6]
>>> x == y
True
>>> x == z
True
>>> x is y
True
>>> x is z
False
>>>
>>> print id(x)
3075326572
>>> print id(y)
3075326572
>>> print id(z)
3075328140
技术分享图片

 前三个例子都是True,这什么最后一个是False呢?x、y和z的值是相同的,所以前两个是True没有问题。至于最后一个为什么是False,看看三个对象的id分别是什么就会明白了。

 下面再来看一个例子,例3中同一类型下的a和b的(a==b)都是为True,而(a is b)则不然。

>>> a = 1 #a和b为数值类型
>>> b = 1
>>> a is b
True
>>> id(a)
14318944
>>> id(b)
14318944
>>> a = ‘cheesezh‘ #a和b为字符串类型
>>> b = ‘cheesezh‘
>>> a is b
True
>>> id(a)
42111872
>>> id(b)
42111872
>>> a = (1,2,3) #a和b为元组类型
>>> b = (1,2,3)
>>> a is b
False
>>> id(a)
15001280
>>> id(b)
14790408
>>> a = [1,2,3] #a和b为list类型
>>> b = [1,2,3]
>>> a is b
False
>>> id(a)
42091624
>>> id(b)
42082016
>>> a = {‘cheese‘:1,‘zh‘:2} #a和b为dict类型
>>> b = {‘cheese‘:1,‘zh‘:2}
>>> a is b
False
>>> id(a)
42101616
>>> id(b)
42098736
>>> a = set([1,2,3])#a和b为set类型
>>> b = set([1,2,3])
>>> a is b
False
>>> id(a)
14819976
>>> id(b)
14822256
技术分享图片

 通过例3可看出,is同一性运算符只有数值型和字符串型的情况下,a is b才为True,当a和b是tuple(元祖),list,dict或set型时,a is b为False。

 

 

深浅拷贝用法来自copy模块。

 导入模块:import copy

 浅拷贝:copy.copy

 深拷贝:copy.deepcopy

 字面理解:浅拷贝指仅仅拷贝数据集合的第一层数据深拷贝指拷贝数据集合的所有层。所以对于只有一层的数据集合来说深浅拷贝的意义是一样的,比如字符串,数字,还有仅仅一层的字典、列表、元祖等.

 对于以下数据深浅拷贝的意义是一样的(因为数据类型中只有一层):

name = ‘beijing‘   #字符串
age = 12  #数字
list1 = [1,2,3,4]  #列表
dic1 = {‘name‘:‘beijing‘,‘age‘:20}  #字典

从内存地址来理解深浅拷贝:

深浅拷贝:

  字符串,数字的深浅拷贝

>>> import copy
>>> name="hahah"   #字符串
>>> name1=copy.copy(name)
>>>
>>> name2=copy.deepcopy(name)
>>> print(id(name),id(name1),id(name2))
11577192 11577192 11577192


>>> sum=111   #数字
>>> sum1=copy.copy(sum)
>>>
>>> sum2=copy.deepcopy(sum)
>>> print(id(sum),id(sum1),id(sum2))
503865568 503865568 503865568
技术分享图片

 

技术分享图片

 

如上图,对于数字和字符串的深浅拷贝都只是将变量的索引指向了原来的内存地址,例如在sum,sum1,sum2三个变量中,无论修改任意其中一个变量,只是将其指向了另一个内存地址,其他两个变量不会变,字符串同理。因此,对于 数字 和 字符串 而言,赋值、浅拷贝和深拷贝无意义,因为其永远指向同一个内存地址。

 

  字典(列表)的深浅拷贝

 

  赋值:

 

import copy
n1 = {‘k1‘:‘wu‘,‘k2‘:123,‘k3‘:[‘alex‘,678]}
n2 = n1

 

技术分享图片

 

  浅拷贝:

 

import copy
n1 = {‘k1‘:‘wu‘,‘k2‘:123,‘k3‘:[‘alex‘,678]}
n3 = copy.copy(n1)

 

技术分享图片

 

  深拷贝:

 

import copy
n1 = {‘k1‘:‘wu‘,‘k2‘:123,‘k3‘:[‘alex‘,678]}
n4 = copy.deepcopy(n1)

 

技术分享图片

 

 

 

 

 

深浅拷贝的应用场景

 


 

  比如在CMDB系统中,我们定义了一个报警模版call给所有的服务器使用,此时有一批特殊应用的服务器需要不通的报警参数,我们既不想单独新建模版来一个一个添加报警参数,又不想修改默认模版而影响其他机器的报警阈值。此时我们就需要用深拷贝来完成。示例如下:

 

  默认模版:

 

call = {
    ‘cpu‘:80,
    ‘mem‘:80,
    ‘disk‘:80
}

 

此时的特殊模版需求是cpu报警阀值要改成75,而不影响默认模版使用

 

代码如下:

 

技术分享图片
import copy
#默认模版
call = {
    ‘cpu‘:[80,],
    ‘mem‘:[80,],
    ‘disk‘:[80,]
}

#新模板
new_call = copy.deepcopy(call)
#修改新模版
new_call[‘cpu‘] = 75
#查看新旧模版的值
print(‘新的模版为:%s‘ %(new_call))
print(‘默认模版为:%s‘ %(call))

#打印结果:
#新的模版为:{‘mem‘: 80, ‘disk‘: 80, ‘cpu‘: 75}
#默认模版为:{‘mem‘: 80, ‘disk‘: 80, ‘cpu‘: 80}

#上面的代码显示我们只改了新的模版,而默认模版并没有修改,并且我们用了copy而不是单独新建模版。
技术分享图片

 

假设我们用浅copy来做结果是这样的:

 

技术分享图片
import copy
#默认模版
call = {
    ‘cpu‘:[80,],
    ‘mem‘:[80,],
    ‘disk‘:[80,]
}

#新模板
new_call = copy.copy(call)
#修改新模版
new_call[‘cpu‘] = 75
#查看新旧模版的值
print(‘新的模版为:%s‘ %(new_call))
print(‘默认模版为:%s‘ %(call))
#打印的结果:
#新的模版为:{‘mem‘: [80], ‘disk‘: [80], ‘cpu‘: [75]}
#默认模版为:{‘mem‘: [80], ‘disk‘: [80], ‘cpu‘: [75]}

#默认模版和新模版都被修改了,显然这不是我们要的结果
技术分享图片

 

分析原因:深拷贝的时候python将字典的所有数据在内存中新建了一份,所以如果你修改新的模版的时候老模版不会变。相反,在浅copy 的时候,python仅仅将最外层的内容在内存中新建了一份出来,字典第二层的列表并没有在内存中新建,所以你修改了新模版,默认模版也被修改了。

 

 

 

 

 Python的GC模块主要运用了“引用计数”(reference counting)来跟踪和回收垃圾。在引用计数的基础上,还可以通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用的问题。通过“分代回收”(generation collection)以空间换取时间来进一步提高垃圾回收的效率。

 

所以可知道:引用计数--跟踪和回收垃圾

 

        标记-清除--解决容器对象可能产生的循环引用的问题

 

        分代回收--以空间换取时间来进一步提高垃圾回收的效率

 

1、引用计数

 

  在Python中,大多数对象的生命周期都是通过对象的引用计数来管理的。从广义上来讲,引用计数也是一种垃圾收集机制,而且也是一种最直观,最简单的垃圾收集技术。

 

  原理:当一个对象的引用被创建或者复制时,对象的引用计数加1;当一个对象的引用被销毁时,对象的引用计数减1;当对象的引用计数减少为0时,就意味着对象已经没有被任何人使用了,可以将其所占用的内存释放了。

 

  虽然引用计数必须在每次分配和释放内存的时候加入管理引用计数的动作,然而与其他主流的垃圾收集技术相比,引用计数有一个最大的优点,即“实时性”,任何内存,一旦没有指向它的引用,就会立即被回收。而其他的垃圾收集计数必须在某种特殊条件下(比如内存分配失败)才能进行无效内存的回收。

 

  引用计数机制执行效率问题:引用计数机制所带来的维护引用计数的额外操作与Python运行中所进行的内存分配和释放,引用赋值的次数是成正比的。而这点相比其他主流的垃圾回收机制,比如“标记-清除”,“停止-复制”,是一个弱点,因为这些技术所带来的额外操作基本上只是与待回收的内存数量有关。

 

  如果说执行效率还仅仅是引用计数机制的一个软肋的话,那么很不幸,引用计数机制还存在着一个致命的弱点,正是由于这个弱点,使得侠义的垃圾收集从来没有将引用计数包含在内,能引发出这个致命的弱点就是循环引用(也称交叉引用)。

 

问题说明

 

  循环引用可以使一组对象的引用计数不为0,然而这些对象实际上并没有被任何外部对象所引用,它们之间只是相互引用。这意味着不会再有人使用这组对象,应该回收这组对象所占用的内存空间,然后由于相互引用的存在,每一个对象的引用计数都不为0,因此这些对象所占用的内存永远不会被释放。比如:

 

技术分享图片
a = []
b = []
a.append(b)
b.append(b)
print(a)   # [[[...]]]
print(b)   # [[...]]
技术分享图片

 

这一点是致命的,这与手动进行内存管理所产生的内存泄露毫无区别。

 

要解决这个问题,Python引入了其他的垃圾收集机制来弥补引用计数的缺陷:“标记-清除”,“分代回收”两种收集技术。

 

2、标记-清除

 

  标记-清除”是为了解决循环引用的问题。可以包含其他对象引用的容器对象(比如:list,set,dict,class,instance)都可能产生循环引用。

 

  我们必须承认一个事实,如果两个对象的引用计数都为1,但是仅仅存在他们之间的循环引用,那么这两个对象都是需要被回收的,也就是说,它们的引用计数虽然表现为非0,但实际上有效的引用计数为0。我们必须先将循环引用摘掉,那么这两个对象的有效计数就现身了。假设两个对象为A、B,我们从A出发,因为它有一个对B的引用,则将B的引用计数减1;然后顺着引用达到B,因为B有一个对A的引用,同样将A的引用减1,这样,就完成了循环引用对象间环摘除。

 

  但是这样就有一个问题,假设对象A有一个对象引用C,而C没有引用A,如果将C计数引用减1,而最后A并没有被回收,显然,我们错误的将C的引用计数减1,这将导致在未来的某个时刻出现一个对C的悬空引用。这就要求我们必须在A没有被删除的情况下复原C的引用计数,如果采用这样的方案,那么维护引用计数的复杂度将成倍增加。

 

  原理:“标记-清除”采用了更好的做法,我们并不改动真实的引用计数,而是将集合中对象的引用计数复制一份副本,改动该对象引用的副本。对于副本做任何的改动,都不会影响到对象生命走起的维护。

 

  这个计数副本的唯一作用是寻找root object集合(该集合中的对象是不能被回收的)。当成功寻找到root object集合之后,首先将现在的内存链表一分为二,一条链表中维护root object集合,成为root链表,而另外一条链表中维护剩下的对象,成为unreachable链表。之所以要剖成两个链表,是基于这样的一种考虑:现在的unreachable可能存在被root链表中的对象,直接或间接引用的对象,这些对象是不能被回收的,一旦在标记的过程中,发现这样的对象,就将其从unreachable链表中移到root链表中;当完成标记后,unreachable链表中剩下的所有对象就是名副其实的垃圾对象了,接下来的垃圾回收只需限制在unreachable链表中即可。

 

3、分代回收

 

  背景:分代的垃圾收集技术是在上个世纪80年代初发展起来的一种垃圾收集机制,一系列的研究表明:无论使用何种语言开发,无论开发的是何种类型,何种规模的程序,都存在这样一点相同之处。即:一定比例的内存块的生存周期都比较短,通常是几百万条机器指令的时间,而剩下的内存块,起生存周期比较长,甚至会从程序开始一直持续到程序结束。

 

  从前面“标记-清除”这样的垃圾收集机制来看,这种垃圾收集机制所带来的额外操作实际上与系统中总的内存块的数量是相关的,当需要回收的内存块越多时,垃圾检测带来的额外操作就越多,而垃圾回收带来的额外操作就越少;反之,当需回收的内存块越少时,垃圾检测就将比垃圾回收带来更少的额外操作。为了提高垃圾收集的效率,采用“空间换时间的策略”。

 

  原理:将系统中的所有内存块根据其存活时间划分为不同的集合,每一个集合就成为一个“代”,垃圾收集的频率随着“代”的存活时间的增大而减小。也就是说,活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率。那么如何来衡量这个存活时间:通常是利用几次垃圾收集动作来衡量,如果一个对象经过的垃圾收集次数越多,可以得出:该对象存活时间就越长。

 

 

Python的每个对象都分为可变和不可变

 

  可变:列表、字典

 

  不可变:数字、字符串、元祖

 

  对不可变类型的变量重新赋值,实际上是重新创建一个不可变类型的对象,并将原来的变量重新指向新创建的对象(如果没有其他变量引用原有对象的话(即引用计数为0),原有对象就会被回收)。

 

不可变类型

 

  以int类型为例:实际上 i += 1 并不是真的在原有的int对象上+1,而是重新创建一个value为6的int对象,i引用自这个新的对象。

 

技术分享图片
>>> i = 5
>>> i += 1
>>> i
6
>>> id(i)
140243713967984

#  通过id函数查看变量i的内存地址进行验证(使用hex(id(i)) 可以查看16进制的内存地址)


>>> i += 1
>>> i
7
>>> id(i)
140243713967960
技术分享图片

 

可以看到执行 i += 1 时,内存地址都会变化,因为int 类型是不可变的。

 

再改改代码,但多个int类型的变量值相同时,看看它们内存地址是否相同。

 

技术分享图片
>>> i = 5
>>> j = 5
>>> id(i)
140656970352216
>>> id(j)
140656970352216
>>> k = 5
>>> id(k)
140656970352216
>>> x = 6
>>> id(x)
140656970352192
>>> y = 6
>>> id(y)
140656970352192
>>> z = 6
>>> id(z)
140656970352192
技术分享图片

 

对于不可变类型int,无论创建多少个不可变类型,只要值相同,都指向同个内存地址。同样情况的还有比较短的字符串。

 

对于其他类型则不同,以浮点类型为例,从代码运行结果可以看出它是个不可变类型:对i的值进行修改后,指向新的内存地址。

 

技术分享图片
>>> i = 1.5
>>> id(i)
140675668569024
>>> i = i + 1.7
>>> i
3.2
>>> id(i)
140675668568976
技术分享图片

 

修改代码声明两个相同值的浮点型变量,查看它们的id,发现它们并不是指向同个内存地址,这点和int类型不同(这方面涉及Python内存管理机制,Python对int类型和较短的字符串进行了缓存,无论声明多少个值相同的变量,实际上都指向同个内存地址。)

 

技术分享图片
>>> i = 2.5
>>> id(i)
140564351733040
>>> j = 2.5
>>> id(j)
140564351733016
技术分享图片

 

可变类型

 

可变类型的话,以list为例。list在append之后,还是指向同个内存地址,因为list是可变类型,可以在原处修改。

 

技术分享图片
>>> a = [1, 2, 3]
>>> id(a)
4385327224
>>> a.append(4)
>>> id(a)
4385327224
技术分享图片

 

改改代码,当存在多个值相同的不可变类型变量时,看看它们是不是跟可变类型一样指向同个内存地址

 

技术分享图片
>>> a = [1, 2, 3]
>>> id(a)
4435060856
>>> b = [1, 2, 3]
>>> id(b)
4435102392
技术分享图片

 

从运行结果可以看出,虽然a、b的值相同,但是指向的内存地址不同。我们也可以通过b = a 的赋值语句,让他们指向同个内存地址:

 

技术分享图片
>>> a = [1, 2, 3]
>>> id(a)
4435060856
>>> b = [1, 2, 3]
>>> id(b)
4435102392
>>> b = a
>>> id(b)
4435060856
技术分享图片

 

这个时候需要注意,因为a、b指向同个内存地址,而a、b的类型都是List,可变类型,对a、b任意一个List进行修改,都会影响另外一个List的值。

 

技术分享图片
>>> a = [1, 2, 3]
>>> id(a)  #4435060856
>>> b = [1, 2, 3]
>>> id(b)  #4435102392
>>> b = a
>>> id(b)  #4435060856
>>> b.append(4)
>>> a
[1, 2, 3, 4]
>>> b
[1, 2, 3, 4]
>>> id(a)  #4435060856
>>> id(b)  #4435060856
技术分享图片

 

代码中,b变量append(4),对a变量也是影响的。输出他们的内存地址,还是指向同个内存地址。

 

 

 

总结:

 

  1、对于不可变类型,无论创建多少个不可变类型,只要值相同,都指向同个内存地址(若值不同,那么一定指向不同的内存地址)。

 

    2、对于可变类型,无论创建多少个可变类型,只要值相同,都不指向同个内存地址(除非进行复制操作,那么他们将会指向同一个地址)。

 

 

 

 

 

 Python是如何进行类型转换的?

技术分享图片

 

 

通俗的说..都是用在一堆数据(比如一个列表)上..

 

map是用同样方法把所有数据都改成别的..字面意思是映射..比如把列表的每个数都换成其平方..

 

reduce是用某种方法依次把所有数据丢进去最后得到一个结果..字面意思是化简..比如计算一个列表所有数的和的过程,就是维持一个部分和然后依次把每个数加进去..

 

filter是筛选出其中满足某个条件的那些数据..字面意思是过滤..比如挑出列表中所有奇数..

 

技术分享图片
>>> map(lambda x:x*x,[0,1,2,3,4,5,6])
[0, 1, 4, 9, 16, 25, 36]
>>> reduce(lambda x,y:x+y,[0,1,2,3,4,5,6])
21
>>> filter(lambda x:x&1,[0,1,2,3,4,5,6])
[1, 3, 5]
技术分享图片

 

 

1、Django

 

2、pip

 

3、pillow-python

 

4、pygame

 

5、pyMysql

 

6、pytz

 

7、opencv-python

 

8、numpy

 

 

 

1、贪婪匹配

总是尝试匹配尽可能多的字符

2、非贪婪匹配

是尝试匹配尽可能少的字符

技术分享图片
import re

secret_code = ‘hadkfalifexxIxxfasdjifja134xxlovexx23345sdfxxyouxx8dfse‘
b = re.findall(‘xx.*xx‘,secret_code)   # 贪婪匹配 
print (b) # [‘xxIxxfasdjifja134xxlovexx23345sdfxxyouxx‘]
c = re.findall(‘xx.*?xx‘,secret_code)  # 非贪婪匹配
print(c) # [‘xxIxx‘, ‘xxlovexx‘, ‘xxyouxx‘]
技术分享图片

贪婪格式:xx.*xx

非贪婪格式:xx.*?xx

区别重点在:.* 和 .*?

 

 

 

技术分享图片
a=[i%2 for i in range(10) ]
print(a)  # 因为 [] 为列表  所以会有结果为[0, 1, 0, 1, 0, 1, 0, 1, 0, 1] 

b=(i%2 for i in range(10))
print(b) # 因为()为元祖 所以会有结果为 <generator object <genexpr> at 0x0000000000645D00>
c=list(b) # 将元祖转换格式为列表
print(c)  # 打印c,结果为 [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
技术分享图片

 

 

print(1 or 2) # 1
print(1 and 2) # 2
print(1<(2==2)) # False 因为2==2为True,而True表示为1,False表示为0,所以1<1,结果为False
print(1<2==2) # True  但是值为什么是True?大神看到请解释一下,谢谢!

 

 

 


 Python里面如何实现tuple和list的转换

 
    1. #From list to Tuple                   
    2. tuple(a_list)     
    3.   
    4. #From Tuple to List  
    5. def to_list(t):   
    6.     return [i if not isinstance(i,tuple) else to_list(i) for i in t]  

 

那我们先通过程序看看这个函数有什么坑吧!

 

技术分享图片
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]

 

函数的第二个默认参数是一个list,当第一次执行的时候实例化了一个list,第二次执行还是用第一次执行的时候实例化的地址存储,所以三次执行的结果就是 [1, 1, 1] ,想每次执行只输出[1] ,默认参数应该设置为None。

 

 

 

 

a=[‘1‘,‘2‘,‘3‘]
b=[int(i) for i in a]
print(b)

输出为:[1, 2, 3]

 

使用set()方法!

list0=[‘b‘,‘c‘, ‘d‘,‘b‘,‘c‘,‘a‘,‘a‘]

print(sorted(set(list0),key=list0.index)) # sorted output

 

 在函数的内部,通过global声明,使在函数内部中设置一个全局变量,这个全局变量可以在任意的函数中进行调用!

技术分享图片
SOLR_URL=‘http://solr.org‘

def tt():
    global SOLR_URL
    SOLR_URL=SOLR_URL+‘#aa‘

def aa():
    if __name__==‘__main__‘:
        tt()
        print(SOLR_URL)
aa()  # http://solr.org#aa

 

 

栈和队列是两种基本的数据结构,同为容器类型。两者根本的区别在于: 
stack:后进先出

 

技术分享图片

 

queue:先进先出

 

技术分享图片

 

PS:stack和queue是不能通过查询具体某一个位置的元素而进行操作的。但是他们的排列是按顺序的

 

对于stack我们可以使用python内置的list实现,因为list是属于线性数组,在末尾插入和删除一个元素所使用的时间都是O(1),这非常符合stack的要求。当然,我们也可以使用链表来实现。

 

stack的实现代码(使用python内置的list),实现起来是非常的简单,就是list的一些常用操作

 

技术分享图片
class Stack(object):
    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 is_empty(self): # 如果栈为空
        return bool(self.stack)

    def top(self): 
        #取出目前stack中最新的元素
        return self.stack[-1]
技术分享图片

 

我们定义如下的链表来实现队列数据结构:

 

技术分享图片

 

定义一个头结点,左边指向队列的开头,右边指向队列的末尾,这样就可以保证我们插入一个元素和取出一个元素都是O(1)的操作,使用这种链表实现stack也是非常的方便。实现代码如下:

 

 

 

技术分享图片
class Head(object):
    def __init__(self):
        self.left = None
        self.right = None

class Node(object):
    def __init__(self, value):
        self.value = value
        self.next = None

class Queue(object):
    def __init__(self):
        #初始化节点
        self.head = Head()

    def enqueue(self, value):
        #插入一个元素
        newnode = Node(value)
        p = self.head
        if p.right:
            #如果head节点的右边不为None
            #说明队列中已经有元素了
            #就执行下列的操作
            temp = p.right
            p.right = newnode
            temp.next = newnode
        else:
            #这说明队列为空,插入第一个元素
            p.right = newnode
            p.left = newnode

    def dequeue(self):
        #取出一个元素
        p = self.head
        if p.left and (p.left == p.right):
            #说明队列中已经有元素
            #但是这是最后一个元素
            temp = p.left
            p.left = p.right = None
            return temp.value
        elif p.left and (p.left != p.right):
            #说明队列中有元素,而且不止一个
            temp = p.left
            p.left = temp.next
            return temp.value

        else:
            #说明队列为空
            #抛出查询错误
            raise LookupError(‘queue is empty!‘)

    def is_empty(self):
        if self.head.left:
            return False
        else:
            return True

    def top(self):
        #查询目前队列中最早入队的元素
        if self.head.left:
            return self.head.left.value
        else:
            raise LookupError(‘queue is empty!‘)
技术分享图片

 

 Python的字符串格式化常用的有三种!

   第一种最方便的

  缺点:需一个个的格式化 

print(‘hello %s and %s‘%(‘df‘,‘another df‘))

 

  第二种最好用的

   优点:不需要一个个的格式化,可以利用字典的方式,缩短时间

 print(‘hello %(first)s and %(second)s‘%{‘first‘:‘df‘ , ‘second‘:‘another df‘})

  第三种最先进的

     优点:可读性强

 print(‘hello {first} and {second}‘.format(first=‘df‘,second=‘another df‘))

 

 

迭代器:是访问集合元素的一种方式,从集合的第一个元素开始访问,直到所有元素被访问结束。其优点是不需要事先准备好整个迭代过程中的所有元素,仅在迭代到某个元素时才开始计算该元素。适合遍历比较巨大的集合。__iter__():方法返回迭代器本身, __next__():方法用于返回容器中下一个元素或数据。

 

生成器:带有yield的函数不再是一个普通函数,而是一个生成器。当函数被调用时,返回一个生成器对象。不像一般函数在生成值后退出,生成器函数在生成值后会自动挂起并暂停他们的执行状态。

 

‘‘‘迭代器‘‘‘
print(‘for x in iter([1, 2, 3, 4, 5]):‘)
for x in iter([1, 2, 3, 4, 5]):
    print(x)

‘‘‘生成器‘‘‘
def myyield(n):
    while n>0:
        print("开始生成...:")
        yield n
        print("完成一次...:")
        n -= 1
for i in myyield(4):
    print("遍历得到的值:",i)

 

 

技术分享图片
data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35]

def binary_search(dataset,find_num):
    if len(dataset) > 1:
        mid = int(len(dataset) / 2)
        if dataset[mid] == find_num:  # find it
            print("找到数字", dataset[mid])
        elif dataset[mid] > find_num:  # 找的数在mid左面
            print("\033[31;1m找的数在mid[%s]左面\033[0m" % dataset[mid])
            return binary_search(dataset[0:mid], find_num)
        else:  # 找的数在mid右面
            print("\033[32;1m找的数在mid[%s]右面\033[0m" % dataset[mid])
            return binary_search(dataset[mid + 1:], find_num)
    else:
        if dataset[0] == find_num:  # find it
            print("找到数字啦", dataset[0])
        else:
            print("没的分了,要找的数字[%s]不在列表里" % find_num)

binary_search(data,20)
技术分享图片

技术分享图片

 

 

 

再说说闭包之前,先说一说什么是外函数,什么是内函数?

  外函数:函数A的内部定义了函数B,那么函数A就叫做外函数

  内函数:函数B就叫做内函数

什么是闭包?

  在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。

技术分享图片

  一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

 

技术分享图片
def outer(a):
    b = 10
    def inner():
        print(a+b)
    return inner

if __name__ == ‘__main__‘:
    demo = outer(5)
    demo()
    demo2 = outer(7)
    demo2()
技术分享图片

 

 

ys模块主要是用于提供对python解释器相关的操作

 

函数

 

 

常用功能

 

技术分享图片
sys.arg 获取位置参数
print(sys.argv)

执行该脚本,加参数的打印结果
python3 m_sys.py  1 2 3 4 5

[‘m_sys.py‘, ‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘]
可以发现 sys.arg返回的是整个位置参数,类似于shell的$0 $1...
sys.exit(n) 程序退出,n是退出是返回的对象
sys.version 获取python版本
>>> sys.version
‘3.5.1 (v3.5.1:37a07cee5969, Dec  5 2015, 21:12:44) \n[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]‘
sys.path 返回模块的搜索路径列表,可通过添加自定义路径,来添加自定义模块
>>> sys.path
[‘‘, ‘/Library/Frameworks/Python.framework/Versions/3.5/lib/python35.zip‘, ‘/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5‘, ‘/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/plat-darwin‘, ‘/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/lib-dynload‘, ‘/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages‘]

sys.platform 返回当前系统平台 linux平台返回linux,windows平台返回win32,MAC返回darwin
>>> sys.platform
‘darwin
sys.stdout.write() 输出内容
>>> sys.stdout.write(‘asd‘)
asd3
>>> sys.stdout.write(‘asd‘)
asd3
>>> sys.stdout.write(‘as‘)
as2
技术分享图片

 

应用: 

进度条:

技术分享图片
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#pyversion:python3.5
#owner:fuzj


"""
sys 和python解析器相关
"""

import sys
import time



def view_bar(num,total):

    rate = num / total
    rate_num =  int(rate * 100)
    #r = ‘\r %d%%‘ %(rate_num)
    r = ‘\r%s>%d%%‘ % (‘=‘ * rate_num, rate_num,)
    sys.stdout.write(r)
    sys.stdout.flush


if __name__ == ‘__main__‘:
    for i in range(0, 101):
        time.sleep(0.1)
        view_bar(i, 100)
效果:

====================================================================================================>100%
技术分享图片

 

 os模块

 OS模块是Python标准库中的一个用于访问操作系统功能的模块,使用OS模块中提供的接口,可以实现跨平台访问

 用于提供系统级别的操作

 

 

答:random模块

 

随机整数:random.randint(a,b):返回随机整数x,a<=x<=b

 

random.randrange(start,stop,[,step]):返回一个范围在(start,stop,step)之间的随机整数,不包括结束值。

 

随机实数:random.random( ):返回0到1之间的浮点数

 

random.uniform(a,b):返回指定范围内的浮点数。

 
技术分享图片
import random # 随机模块

data = list(range(10))
print(data)  # 打印有序的列表 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
random.shuffle(data) # 使有序变为无序 
print(data) # 打印无序的列表 [4, 2, 5, 1, 6, 3, 9, 8, 0, 7]
技术分享图片

 

 

 

 

若想利用python删除windows里的文件,这里需要使用os模块!那接下来就看看利用os模块是如何删除文件的!

 

具体实现方法如下!

 

os.remove(path)

 

删除文件 path. 如果path是一个目录, 抛出 OSError错误。如果要删除目录,请使用rmdir().

 

remove() 同 unlink() 的功能是一样的 
在Windows系统中,删除一个正在使用的文件,将抛出异常。在Unix中,目录表中的记录被删除,但文件的存储还在。

 

技术分享图片
import os
my_file = ‘D:/text.txt‘ # 文件路径
if os.path.exists(my_file): # 如果文件存在
    #删除文件,可使用以下两种方法。
    os.remove(my_file) # 则删除
    #os.unlink(my_file)
else:
    print(‘no such file:%s‘%my_file)
技术分享图片

 

技术分享图片

 

os.removedirs(path) 
递归地删除目录。类似于rmdir(), 如果子目录被成功删除, removedirs() 将会删除父目录;但子目录没有成功删除,将抛出错误。

 

例如, os.removedirs(“foo/bar/baz”) 将首先删除baz目录,然后再删除bar和 foo, 如果他们是空的话,则子目录不能成功删除,将抛出 OSError异常

 

os.rmdir(path) 
删除目录 path,要求path必须是个空目录,否则抛出OSError错误

 

技术分享图片
import os
for root, dirs, files in os.walk(top, topdown=False):
    for name in files:
        os.remove(os.path.join(root, name))
    for name in dirs:
        os.rmdir(os.path.join(root, name))
技术分享图片

 

方法2: 
代码如下:

 

import shutil
shutil.rmtree()

 

 

什么是封装?

 

  所谓的面向对象就是将我们的程序模块化,对象化,把具体事物的特性属性和通过这些属性来实现一些动作的具体方法放到一个类里面,这就是封装。封装是我们所说的面相对象编程的特征之一。除此之外还有继承和多态。

 

什么是继承?

 

  继承有点类似与我们生物学上的遗传,就是子类的一些特征是来源于父类的,儿子遗传了父亲或母亲的一些性格,或者相貌,又或者是运动天赋。有点种瓜得瓜种豆得豆的意思。面向对象里的继承也就是父类的相关的属性,可以被子类重复使用,子类不必再在自己的类里面重新定义一回,父类里有点我们只要拿过来用就好了。而对于自己类里面需要用到的新的属性和方法,子类就可以自己来扩展了。

 

什么是多态?

 

  我们在有一些方法在父类已经定义好了,但是子类我们自己再用的时候,发现,其实,我们的虽然都是计算工资的,但是普通员工的工资计算方法跟经理的计算方法是不一样的,所以这个时候,我们就不能直接调用父类的这个计算工资的方法了。这个时候我们就需要用到面向对象的另一个特性,多态。我们要在子类里面把父类里面定义计算工资的方法在子类里面重新实现一遍。多态包含了重载重写

 

什么是重写?

 

  重写很简单就是把子类从父亲类里继承下来的方法重新写一遍,这样,父类里相同的方法就被覆盖了,当然啦,你还是可以通过super.CaculSalary方法来调用父类的工资计算方法。

 

什么是重载?

 

  重载就是类里面相同方法名,不同形参的情况,可以是形参类型不同或者形参个数不同,或者形参顺序不同,但是不能使返回值类型不同。

 

 

继承的优点:

 

  1、建造系统中的类,避免重复操作

 

  2、新类经常是基于已经存在的类,这样就可以提升代码的复用程度

 

继承的特点:

 

  1、在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。有别于C#

 

  2、在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别于在类中调用普通函数时并不需要带上self参数

 

  3、Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。

 

 

什么是super?

 

  super() 函数是用于调用父类(超类)的一个方法。
  super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。
  MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。

 

语法

 

以下是 super() 方法的语法:

 

super(type[, object-or-type])

 

参数

 

  ·type -- 类。

 

  ·object-or-type -- 类,一般是 self

 

Python3.x 和 Python2.x 的一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :
Python3.x 实例:

 

class A:
    pass
class B(A):
    def add(self, x):
        super().add(x)

 

Python2.x 实例:

 

class A(object):   # Python2.x 记得继承 object
    pass
class B(A):
    def add(self, x):
        super(B, self).add(x)

 

具体应用示例:

 

举个例子

 

class Foo:  
  def bar(self, message):  
    print(message)  
>>> Foo().bar("Hello, Python.")  
Hello, Python.

 

  当存在继承关系的时候,有时候需要在子类中调用父类的方法,此时最简单的方法是把对象调用转换成类调用,需要注意的是这时self参数需要显式传递,例如:

 

技术分享图片
class FooParent:  
  def bar(self, message):  
    print(message)  
class FooChild(FooParent):  
  def bar(self, message):  
    FooParent.bar(self, message)  
>>> FooChild().bar("Hello, Python.")  
Hello, Python.
技术分享图片

 

  这样做有一些缺点,比如说如果修改了父类名称,那么在子类中会涉及多处修改,另外,Python是允许多继承的语言,如上所示的方法在多继承时就需要重复写多次,显得累赘。为了解决这些问题,Python引入了super()机制,例子代码如下:

 

技术分享图片
class FooParent:  
  def bar(self, message):  
    print(message)  
class FooChild(FooParent):  
  def bar(self, message):  
    super(FooChild, self).bar(message)  
  
>>> FooChild().bar("Hello, Python.")  
Hello, Python
技术分享图片

 

  表面上看 super(FooChild, self).bar(message)方法和FooParent.bar(self, message)方法的结果是一致的,实际上这两种方法的内部处理机制大大不同,当涉及多继承情况时,就会表现出明显的差异来,直接给例子:
代码一:

 

技术分享图片
class A:  
  def __init__(self):  
    print("Enter A")  
    print("Leave A")  
class B(A):  
  def __init__(self):  
    print("Enter B")  
    A.__init__(self)  
    print("Leave B")  
class C(A):  
  def __init__(self):  
    print("Enter C")  
    A.__init__(self)  
    print("Leave C")  
class D(A):  
  def __init__(self):  
    print("Enter D")  
    A.__init__(self)  
    print("Leave D")  
class E(B, C, D):  
  def __init__(self):  
    print("Enter E")  
    B.__init__(self)  
    C.__init__(self)  
    D.__init__(self)  
    print("Leave E")  
E()
技术分享图片

 

结果为:

 

Enter E
Enter B
Enter A
Leave A
Leave B
Enter C
Enter A
Leave A
Leave C
Enter D
Enter A
Leave A
Leave D
Leave E

 

执行顺序很好理解,唯一需要注意的是公共父类A被执行了多次。

 

代码二:

 

技术分享图片
class A:  
  def __init__(self):  
    print("Enter A")  
    print("Leave A")  
class B(A):  
  def __init__(self):  
    print("Enter B")  
    super(B, self).__init__()  
    print("Leave B")  
class C(A):  
  def __init__(self):  
    print("Enter C")  
    super(C, self).__init__()  
    print("Leave C")  
class D(A):  
  def __init__(self):  
    print("Enter D")  
    super(D, self).__init__()  
    print("Leave D")  
class E(B, C, D):  
  def __init__(self):  
    print("Enter E")  
    super(E, self).__init__()  
    print("Leave E")  
E()
技术分享图片

 

结果:
Enter E
Enter B
Enter C
Enter D
Enter A
Leave A
Leave D
Leave C
Leave B
Leave E

 

  在super机制里可以保证公共父类仅被执行一次,至于执行的顺序,是按照MRO(Method Resolution Order):方法解析顺序 进行的。

 

 

functools模块介绍

 

  functools用于高阶函数:指那些作用于函数或者返回其他函数的函数。通常情况下,只要是可以被当做函数调用的对象就是这个模块的目标。

 

functools模块的功能

 

python 中提供一种用于对函数固定属性的函数(与数学上的偏函数不一样)

 

# 通常会返回10进制
int(‘12345‘)  # print 12345 
 
# 使用参数 返回 8进制
int(‘11111‘, 8)  # print 4681

 

每次都得添加参数比较麻烦, functools提供了partial的方法

 

import functools
 
foo = functools.partial(int, base=8)
 
foo(‘11111‘)  # print 4681

 

通过这种方法生成一个固定参数的新函数
假设要转换大量的二进制字符串,每次都传入int(x, base=2)非常麻烦,于是,我们想到,可以定义一个int2()的函数,默认把base=2传进去:
def int2(x, base=2):
  return int(x, base)

这样,我们转换二进制就非常方便了:

>>> int2(‘1000000‘)
64
>>> int2(‘1010101‘)
85

 

functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2:

 

 

技术分享图片
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2(‘1000000‘)
64
>>> int2(‘1010101‘)
85
技术分享图片

 

所以,简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。

 

注意到上面的新的int2函数,仅仅是把base参数重新设定默认值为2,但也可以在函数调用时传入其他值:

 

>>> int2(‘1000000‘, base=10)
1000000

 

最后,创建偏函数时,实际上可以接收函数对象、*args和**kwargs这3个参数,当传入:

 

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

 

实际上固定了int()函数的关键字参数base,也就是:

 

int2(‘10010‘)

 

相当于:

 

kw = { ‘base‘: 2 }
int(‘10010‘, **kwargs)

 

当传入:

 

max2 = functools.partial(max, 10)

 

实际上会把10作为*args的一部分自动加到左边,也就是:

 

max2(5, 6, 7)

 

相当于:

 

args = (10, 5, 6, 7)
max(*args)

 

这个__new__确实很少见到,先做了解吧.

  1. __new__是一个静态方法,而__init__是一个实例方法.
  2. __new__方法会返回一个创建的实例,而__init__什么都不返回.
  3. 只有在__new__返回一个cls的实例时后面的__init__才能被调用.
  4. 当创建一个新实例时调用__new__,初始化一个实例时用__init__.

 

函数:

 

  函数是封装了一些独立的功能,可以直接调用,python内置了许多函数,同时可以自建函数来使用。

 

方法:

 

  方法和函数类似,同样封装了独立的功能,但是方法是需要通过对象来调用的,表示针对这个对象要做的操作,使用时采用点方法。

 

 

3 @staticmethod和@classmethod

 

Python其实有3个方法,即静态方法(staticmethod),类方法(classmethod)和实例方法,如下:

 

def foo(x):
    print "executing foo(%s)"%(x)

class A(object):
    def foo(self,x):
        print "executing foo(%s,%s)"%(self,x)

    @classmethod
    def class_foo(cls,x):
        print "executing class_foo(%s,%s)"%(cls,x)

    @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)"%x

a=A()

 

这里先理解下函数参数里面的self和cls.这个self和cls是对类或者实例的绑定,对于一般的函数来说我们可以这么调用foo(x),这个函数就是最常用的,它的工作跟任何东西(类,实例)无关.对于实例方法,我们知道在类里每次定义方法的时候都需要绑定这个实例,就是foo(self, x),为什么要这么做呢?因为实例方法的调用离不开实例,我们需要把实例自己传给函数,调用的时候是这样的a.foo(x)(其实是foo(a, x)).类方法一样,只不过它传递的是类而不是实例,A.class_foo(x).注意这里的self和cls可以替换别的参数,但是python的约定是这俩,还是不要改的好.

 

对于静态方法其实和普通的方法一样,不需要对谁进行绑定,唯一的区别是调用的时候需要使用a.static_foo(x)或者A.static_foo(x)来调用.

 

\实例方法类方法静态方法
a = A() a.foo(x) a.class_foo(x) a.static_foo(x)
A 不可用 A.class_foo(x) A.static_foo(x)

 

>>> class MyClass():
...     def __init__(self):
...             self.__superprivate = "Hello"
...             self._semiprivate = ", world!"
...
>>> mc = MyClass()
>>> print mc.__superprivate
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: myClass instance has no attribute ‘__superprivate>>> print mc._semiprivate
, world!
>>> print mc.__dict__
{‘_MyClass__superprivate‘: ‘Hello‘, ‘_semiprivate‘: ‘, world!‘}

__foo__:一种约定,Python内部的名字,用来区别其他用户自定义的命名,以防冲突,就是例如__init__(),__del__(),__call__()这些特殊方法

_foo:一种约定,用来指定变量私有.程序员用来指定私有变量的一种方式.不能用from module import * 导入,其他方面和公有一样访问;

__foo:这个有真正的意义:解析器用_classname__foo来代替这个名字,以区别和其他类相同的命名,它无法直接像公有成员一样随便访问,通过对象名._类名__xxx这样的方式可以访问.

详情见:http://stackoverflow.com/questions/1301346/the-meaning-of-a-single-and-a-double-underscore-before-an-object-name-in-python

或者: http://www.zhihu.com/question/19754941

 

Python 中,一个变量的作用域总是由在代码中被赋值的地方所决定的。

 

当 Python 遇到一个变量的话他会按照这样的顺序进行搜索:

 

本地作用域(Local)→当前作用域被嵌入的本地作用域(Enclosing locals)→全局/模块作用域(Global)→内置作用域(Built-in)

 

 

装饰器是一个工厂函数,接受一个函数作为参数,然后返回一个新函数,其闭包中包含被装饰的函数。有了装饰器,可以提取大量函数中与本身功能无关的类似代码 ( 这块在Flask中用于定义路由的@app.route,就是一个很好的例子),达到代码重用的目的。可应用于插入日志、性能测试、事务处理等方面。

 

def deco(func):
    def warpper(*args, **kwargs):
        print(‘start‘)
        func(*args, **kwargs)
        print(‘end‘)
    return warpper

@deco
def myfunc(parameter):
    print("run with %s" % parameter)

myfunc("something")

 

 

IOError、AttributeError、ImportError、IndentationError、IndexError、KeyError、SyntaxError、NameError分别代表什么异常

IOError:输入输出异常

AttributeError:试图访问一个对象没有的属性

ImportError:无法引入模块或包,基本是路径问题

IndentationError:语法错误,代码没有正确的对齐

IndexError:下标索引超出序列边界

KeyError:试图访问你字典里不存在的键

SyntaxError:Python代码逻辑语法出错,不能执行

NameError:使用一个还未赋予对象的变量

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!