Python编码解析

采菊篱下 发表了文章 • 0 个评论 • 551 次浏览 • 2016-09-09 17:38 • 来自相关话题

一般我们在Python2.7的环境进行Python的编程的时候,一般头部会加#-*- coding:utf-8 -*- ​来声明编码类型为utf-8的编码,那为什么要声明,一定要声明吗?

针对如上问题我们先来讨论另外一个问题,为什么我们可以在显示器上能看到这些文字、数字、图片、字符、等等信息呢?大家都知道计算机本身只能识别 0  1 的组合,他们是怎么展示这些内容的呢?我们怎么和计算机去沟通呢?

如果我们使用0 1 的组合和计算机沟通你还能看到这些内容吗?还有一个问题就是01的组合我相信对于常人类都是没有办法看懂的。

那怎么办?如何让计算机理解我们的语言,并且我们能理解计算机的语言呢?

举个比较形象的例子,中英文词典对照表,这样我们就可以把中英文进行互相的翻译了呢?对不对!同理计算机也是这样的他需要一个标准的对照关系,那么这个标准最早叫什么呢?ASCII表




表格内容大致如下:
有特殊符号、大写字母、小写字母、数字(这里注意下0~9的数字是字符),在这些字符左边都有一个10进制的数字。但是对于10进制来说计算机他也是不能理解的,因为他只能理解0 1 ,但是10进制和2进制的转换就非常容易了!

举例来说:如果我在键盘上按一个A字母的时候那么实际是给计算机传输了一个数字65,通过这样的机制和计算机沟通,有了这个ASCII码表就可以和任何计算机进行沟通了。

这里在看个知识点:计算机中最小的单位是什么?bit   bit就咱们常说一位二进制,一位二进制要么是0 要么是 1

但是bit这个单位太小了,我们用字节(byte)来表示。他们是有换算的规则的(看下面的规则我想大家都不是很陌生对吧):8b = 1B #小b=bit ; 大B=byte
1024B = 1KB
1024KB = 1M
1024M = 1G
1024G = 1T 在存储英文的时候我们至少需要1个字节(一个字母),就是8位(bit),看下ASCII表中1个字节就可以表示所有的英文所需要的字符,是不非常高效!

为什么呢?早期的计算机的空间是非常宝贵的!

那你会发现1个字节8位,他能存储的最大数据是2的8次方-1 = 255,一个字节最多能表示255个字符 那西方国家他们使用了127个字符,那么剩下字符是做什么的呢?就是用来做扩展的,西方人考虑到还有其他国家。所以留下了扩展位。

但是呢有问题,计算机是西方人发明的,如果仅仅支持英文的话,这127个字符完全就可以表示所有英文中能用的的内容了。但是他没有考虑咱们大中国啊!ASCII到了中国之后发现:咱们中国最常用的中文都有6000多个完全不够用啊!

但是怎们办?中国人非常聪明:就在原有的扩展位中,扩展出自己的gbk、gb2312、gb2318字符编码。 

他是怎么扩展的呢?比如说在ASCII码中的128这个位置,这个位置又指定一张单独表,聪明吧! 其他国家也是这样设计的!

中国东亚大国是吧,我们国家比较NB,我要兼容其他国家的常用的编码!比如韩国日本,因为韩国和日本人家都有自己的编码,人家根本就不鸟你,举个例子来说,比如韩国的游戏,在中国下载安装之后会出现乱码的情况?什么鬼?

这种乱码的出现基本上就两种情况:
1、字符编码没有
2、字符编码冲突了,人家在写这个程序的时候指定的字符集和咱们使用的字符集的位置不对。 

你想想不光是亚洲国家这样,欧洲国家,非洲国家都会存在这个问题,基于这个乱象国际互联网组织就说你们各个国家都别搞了,我们给你们搞一个统一的,这个统一的是什么呢Unicode“万国编码”。

Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,规定虽有的字符和符号最少由 16 位来表示(2个字节),即:2 **16 = 65536, 注:此处说的的是最少2个字节,可能更多。

这里还有个问题:使用的字节增加了,那么造成的直接影响就是使用的空间就直接翻倍了!举例还说:同样是ABCD这些字符存储一篇相同的文章,使用ASCII码如果是1M的话,那么Unicode存储至少2M可能还会更多。

为了解决个问题就出现了:UTF-8编码

UTF-8编码:是对Unicode编码的压缩和优化,他不再使用最少使用2个字节,而是将所有的字符和符号进行分类:ascii码中的内容用1个字节保存、欧洲的字符用2个字节保存,东亚的字符用3个字节保存...

通过这种可扩展的方式来存储。

OK 上面了解了:
1、什么ASCII编码
2、什么Unicode编码
3、什么UTF-8编码

回顾下乱码的出现原因:1、没有字符集 2、字符集冲突

回过头来看下为什么需要在第二行加上指定编码呢?在2.x版本的Python中Pyton在解释.py文件的时候,默认是给他一个编码的就是ASCII码,so如果在2.7版本中如果你不指定编码并且在.py文件中写了一个ASCII码中没有的字符就会显示乱码 。

不过这个问题在Python3中就不存在了,因为在Python3中默认就是Unicode编码。

Python编码转换

有一个问题,既然有统一的Unicode编码了,为毛还需要编码转换?大家都统一一个编码不就可以了吗?

不要问我为什么,我问你们个问题,如果世界上出了一种世界语言,你会放弃中文吗?去使用这个世界通用语言吗?这就是个坑,是个遗留问题。

但是虽然以后可能世界语言会慢慢替代咱们常用的语言,大家以后沟通就使用世界语言就不会有沟通障碍了对吧。(就是举个例子)

还有一个情况是什么呢?韩国的游戏到中国来之后,是乱码?结合上一个回答咱们可以猜出:编写这个游戏的人在编写游戏的时候可能根本就没有考虑出口其他国家。那如果没有这个Unicode编码的话,到咱们这里来显示肯定是乱码是吧。

那就得需要通过转码把他们编码集,转换为Unicode(utf-8)编码集。这样他们就可以正常显示韩文了!(这里只是转编码集并不是翻译成中文不要弄混了~~!)

Python3中的编码转换
#在Python3中默认就是unicode编码#!/usr/bin/env python3
# _*_coding:utf-8_*_
# Author: Lucky.chen

tim = '华仔'
#转为UTF-8编码
print(tim.encode('UTF-8'))

#转为GBK编码
print(tim.encode('GBK'))

#转为ASCII编码(报错为什么?因为ASCII码表中没有‘华仔’这个字符集~~)
print(tim.encode('ASCII'))



 
二、Python2.X中的编码转换
#因为在python2.X中默认是ASCII编码,你在文件中指定编码为UTF-8,但是UTF-8如果你想转GBK的话是不能直接转,需要Unicode做一个中间人转换角色。




#!/usr/bin/env python2
# _*_coding:utf-8_*_
# Author: Lucky.chen

import sys
print(sys.getdefaultencoding())


msg = "我爱北京天安门"
msg_gb2312 = msg.decode("utf-8").encode("gb2312")
gb2312_to_gbk = msg_gb2312.decode("gbk").encode("gbk")

print(msg)
print(msg_gb2312)
print(gb2312_to_gbk) 查看全部
一般我们在Python2.7的环境进行Python的编程的时候,一般头部会加#-*- coding:utf-8 -*- ​来声明编码类型为utf-8的编码,那为什么要声明,一定要声明吗?

针对如上问题我们先来讨论另外一个问题,为什么我们可以在显示器上能看到这些文字、数字、图片、字符、等等信息呢?大家都知道计算机本身只能识别 0  1 的组合,他们是怎么展示这些内容的呢?我们怎么和计算机去沟通呢?

如果我们使用0 1 的组合和计算机沟通你还能看到这些内容吗?还有一个问题就是01的组合我相信对于常人类都是没有办法看懂的。

那怎么办?如何让计算机理解我们的语言,并且我们能理解计算机的语言呢?

举个比较形象的例子,中英文词典对照表,这样我们就可以把中英文进行互相的翻译了呢?对不对!同理计算机也是这样的他需要一个标准的对照关系,那么这个标准最早叫什么呢?ASCII表
ascii.gif

表格内容大致如下:
有特殊符号、大写字母、小写字母、数字(这里注意下0~9的数字是字符),在这些字符左边都有一个10进制的数字。但是对于10进制来说计算机他也是不能理解的,因为他只能理解0 1 ,但是10进制和2进制的转换就非常容易了!

举例来说:如果我在键盘上按一个A字母的时候那么实际是给计算机传输了一个数字65,通过这样的机制和计算机沟通,有了这个ASCII码表就可以和任何计算机进行沟通了。

这里在看个知识点:计算机中最小的单位是什么?bit   bit就咱们常说一位二进制,一位二进制要么是0 要么是 1

但是bit这个单位太小了,我们用字节(byte)来表示。他们是有换算的规则的(看下面的规则我想大家都不是很陌生对吧):
8b = 1B  #小b=bit ; 大B=byte
1024B = 1KB
1024KB = 1M
1024M = 1G
1024G = 1T
在存储英文的时候我们至少需要1个字节(一个字母),就是8位(bit),看下ASCII表中1个字节就可以表示所有的英文所需要的字符,是不非常高效!

为什么呢?早期的计算机的空间是非常宝贵的!

那你会发现1个字节8位,他能存储的最大数据是2的8次方-1 = 255,一个字节最多能表示255个字符 那西方国家他们使用了127个字符,那么剩下字符是做什么的呢?就是用来做扩展的,西方人考虑到还有其他国家。所以留下了扩展位。

但是呢有问题,计算机是西方人发明的,如果仅仅支持英文的话,这127个字符完全就可以表示所有英文中能用的的内容了。但是他没有考虑咱们大中国啊!ASCII到了中国之后发现:咱们中国最常用的中文都有6000多个完全不够用啊!

但是怎们办?中国人非常聪明:就在原有的扩展位中,扩展出自己的gbk、gb2312、gb2318字符编码。 

他是怎么扩展的呢?比如说在ASCII码中的128这个位置,这个位置又指定一张单独表,聪明吧! 其他国家也是这样设计的!

中国东亚大国是吧,我们国家比较NB,我要兼容其他国家的常用的编码!比如韩国日本,因为韩国和日本人家都有自己的编码,人家根本就不鸟你,举个例子来说,比如韩国的游戏,在中国下载安装之后会出现乱码的情况?什么鬼?

这种乱码的出现基本上就两种情况:
1、字符编码没有
2、字符编码冲突了,人家在写这个程序的时候指定的字符集和咱们使用的字符集的位置不对。 

你想想不光是亚洲国家这样,欧洲国家,非洲国家都会存在这个问题,基于这个乱象国际互联网组织就说你们各个国家都别搞了,我们给你们搞一个统一的,这个统一的是什么呢Unicode“万国编码”

Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,规定虽有的字符和符号最少由 16 位来表示(2个字节),即:2 **16 = 65536, 注:此处说的的是最少2个字节,可能更多。

这里还有个问题:使用的字节增加了,那么造成的直接影响就是使用的空间就直接翻倍了!举例还说:同样是ABCD这些字符存储一篇相同的文章,使用ASCII码如果是1M的话,那么Unicode存储至少2M可能还会更多。

为了解决个问题就出现了:UTF-8编码

UTF-8编码:是对Unicode编码的压缩和优化,他不再使用最少使用2个字节,而是将所有的字符和符号进行分类:ascii码中的内容用1个字节保存、欧洲的字符用2个字节保存,东亚的字符用3个字节保存...

通过这种可扩展的方式来存储。

OK 上面了解了:
1、什么ASCII编码
2、什么Unicode编码
3、什么UTF-8编码

回顾下乱码的出现原因:1、没有字符集 2、字符集冲突

回过头来看下为什么需要在第二行加上指定编码呢?在2.x版本的Python中Pyton在解释.py文件的时候,默认是给他一个编码的就是ASCII码,so如果在2.7版本中如果你不指定编码并且在.py文件中写了一个ASCII码中没有的字符就会显示乱码 。

不过这个问题在Python3中就不存在了,因为在Python3中默认就是Unicode编码。


Python编码转换


有一个问题,既然有统一的Unicode编码了,为毛还需要编码转换?大家都统一一个编码不就可以了吗?

不要问我为什么,我问你们个问题,如果世界上出了一种世界语言,你会放弃中文吗?去使用这个世界通用语言吗?这就是个坑,是个遗留问题。

但是虽然以后可能世界语言会慢慢替代咱们常用的语言,大家以后沟通就使用世界语言就不会有沟通障碍了对吧。(就是举个例子)

还有一个情况是什么呢?韩国的游戏到中国来之后,是乱码?结合上一个回答咱们可以猜出:编写这个游戏的人在编写游戏的时候可能根本就没有考虑出口其他国家。那如果没有这个Unicode编码的话,到咱们这里来显示肯定是乱码是吧。

那就得需要通过转码把他们编码集,转换为Unicode(utf-8)编码集。这样他们就可以正常显示韩文了!(这里只是转编码集并不是翻译成中文不要弄混了~~!)

Python3中的编码转换
#在Python3中默认就是unicode编码
#!/usr/bin/env python3
# _*_coding:utf-8_*_
# Author: Lucky.chen

tim = '华仔'
#转为UTF-8编码
print(tim.encode('UTF-8'))

#转为GBK编码
print(tim.encode('GBK'))

#转为ASCII编码(报错为什么?因为ASCII码表中没有‘华仔’这个字符集~~)
print(tim.encode('ASCII'))
EncodeError.png

 
二、Python2.X中的编码转换
#因为在python2.X中默认是ASCII编码,你在文件中指定编码为UTF-8,但是UTF-8如果你想转GBK的话是不能直接转,需要Unicode做一个中间人转换角色。
decode.png

#!/usr/bin/env python2
# _*_coding:utf-8_*_
# Author: Lucky.chen

import sys
print(sys.getdefaultencoding())


msg = "我爱北京天安门"
msg_gb2312 = msg.decode("utf-8").encode("gb2312")
gb2312_to_gbk = msg_gb2312.decode("gbk").encode("gbk")

print(msg)
print(msg_gb2312)
print(gb2312_to_gbk)

Python使用正则收集IP信息

Not see︶ 发表了文章 • 0 个评论 • 515 次浏览 • 2016-08-24 14:38 • 来自相关话题

#!/usr/bin/env python

import re
from subprocess import Popen, PIPE

def getIfconfig():
p = Popen(['ifconfig'], stdout=PIPE)
data = p.stdout.read().split('\n\n')
return [i for i in data if i and not i.startswith('lo')]

def parseIfconfig(data):
re_devname = re.compile(r'(br|eth|em|virbr|lo|bond)[\d:]+',re.M)
re_mac = re.compile(r'HWaddr ([0-9A-F:]{17})', re.M)
re_ip = re.compile('inet addr:([\d\.]{7,15})', re.M)
devname = re_devname.search(data)
if devname:
devname = devname.group()
else:
devname = ''
mac = re_mac.search(data)
if mac:
mac = mac.group(1)
else:
mac = ''
ip = re_ip.search(data)
if ip:
ip = ip.group(1)
else:
ip = ''
return {devname: [ip, mac]}

if __name__ == '__main__':
data = getIfconfig()
for i in data:
print parseIfconfig(i) 查看全部
#!/usr/bin/env python

import re
from subprocess import Popen, PIPE

def getIfconfig():
p = Popen(['ifconfig'], stdout=PIPE)
data = p.stdout.read().split('\n\n')
return [i for i in data if i and not i.startswith('lo')]

def parseIfconfig(data):
re_devname = re.compile(r'(br|eth|em|virbr|lo|bond)[\d:]+',re.M)
re_mac = re.compile(r'HWaddr ([0-9A-F:]{17})', re.M)
re_ip = re.compile('inet addr:([\d\.]{7,15})', re.M)
devname = re_devname.search(data)
if devname:
devname = devname.group()
else:
devname = ''
mac = re_mac.search(data)
if mac:
mac = mac.group(1)
else:
mac = ''
ip = re_ip.search(data)
if ip:
ip = ip.group(1)
else:
ip = ''
return {devname: [ip, mac]}

if __name__ == '__main__':
data = getIfconfig()
for i in data:
print parseIfconfig(i)

Python3中异常处理常用的三种方法

小白菜 发表了文章 • 0 个评论 • 590 次浏览 • 2016-08-22 23:13 • 来自相关话题

一般我们我们想捕获python的异常写入到log,做处理的话,一般用try语句来做处理,大概语法如下:try:
语句1
语句2
.
.
语句N
except ........ :
do something .......但是你并不知道"语句1至语句N"哪个会出现什么样的错误,但你还要做异常处理,且想把出现的异常打印出来,并不停止程序的运行,所以在"except ......"语句就起作用了。
 
方法一:捕获所有异常Python2如下:
try:
语句1
语句2
.....
语句N
except Exception,e:
print Exception,":",e

Python3如下:
try:
语句1
语句2
.....
语句N
except Exception as e:
print(Exception, ":", e)例子如下:try:
a = 1
b = a
c = w
except Exception as e:
print(Exception, ":", e)

结果为:
<class 'Exception'> : name 'w' is not defined
 方法二:采用sys模块回溯最后的异常
sys.exc_info() 会返回一个3值元表,其中包含调用该命令时捕获的异常。
这个元表的内容为 (type, value, traceback) ,其中:
type 从获取到的异常中得到类型名称,它是BaseException 的子类;value 是捕获到的异常实例;traceback 是一个 traceback 对象,下面会详述。
import sys
try:
w = abs(-1)
list.append(w)
except:
info = sys.exc_info()
print(info[0], ":", info[1])

# 结果如下:
<class 'TypeError'> : descriptor 'append' requires a 'list' object but received a 'int'sys.last_traceback 包含的内容与 sys.exc_info() 相同,但它主要用于调试,并不总是被定义。
 
三、采用traceback模块查看异常
trackback 模块用来精确模仿 python3 解析器的 stack trace 行为。在程序中应该尽量使用这个模块。
traceback.print_exc() 可以直接打印当前的异常。import traceback
try:
raise
except:
traceback.print_exc()traceback.print_tb() 用来打印上面提到的 trackback 对象。import sys,traceback
try:
raise
except:
t,v,tb = sys.exc_info()
traceback.print_tb(tb)traceback.print_exception() 可以直接打印 sys.exc_info()提供的元表。import sys,traceback
try:
raise
except:
traceback.print_exception(*sys.exc_info())其实,下面两句是等价的:
traceback.print_exc()traceback.print_exception(*sys.exc_info())
 
traceback 提供的参数可以将 print 的内容写入到文件中import traceback
try:
a=b
b=c
except:
f=open("log.txt",'a')
traceback.print_exc(file=f)
f.flush()
f.close()参考:
https://docs.python.org/3/library/traceback.html?highlight=print_tb#traceback.print_exc
https://docs.python.org/3/tutorial/errors.html 查看全部
一般我们我们想捕获python的异常写入到log,做处理的话,一般用try语句来做处理,大概语法如下:
try:
语句1
语句2
.
.
语句N
except ........ :
do something .......
但是你并不知道"语句1至语句N"哪个会出现什么样的错误,但你还要做异常处理,且想把出现的异常打印出来,并不停止程序的运行,所以在"except ......"语句就起作用了。
 
方法一:捕获所有异常
Python2如下:
try:
语句1
语句2
.....
语句N
except Exception,e:
print Exception,":",e

Python3如下:
try:
语句1
语句2
.....
语句N
except Exception as e:
print(Exception, ":", e)
例子如下:
try:
a = 1
b = a
c = w
except Exception as e:
print(Exception, ":", e)

结果为:
<class 'Exception'> : name 'w' is not defined

 方法二:采用sys模块回溯最后的异常
sys.exc_info() 会返回一个3值元表,其中包含调用该命令时捕获的异常。
这个元表的内容为 (type, value, traceback) ,其中:
  • type 从获取到的异常中得到类型名称,它是BaseException 的子类;
  • value 是捕获到的异常实例;
  • traceback 是一个 traceback 对象,下面会详述。

import sys
try:
w = abs(-1)
list.append(w)
except:
info = sys.exc_info()
print(info[0], ":", info[1])

# 结果如下:
<class 'TypeError'> : descriptor 'append' requires a 'list' object but received a 'int'
sys.last_traceback 包含的内容与 sys.exc_info() 相同,但它主要用于调试,并不总是被定义。
 
三、采用traceback模块查看异常
trackback 模块用来精确模仿 python3 解析器的 stack trace 行为。在程序中应该尽量使用这个模块。
traceback.print_exc() 可以直接打印当前的异常。
import traceback
try:
raise
except:
traceback.print_exc()
traceback.print_tb() 用来打印上面提到的 trackback 对象。
import sys,traceback
try:
raise
except:
t,v,tb = sys.exc_info()
traceback.print_tb(tb)
traceback.print_exception() 可以直接打印 sys.exc_info()提供的元表。
import sys,traceback
try:
raise
except:
traceback.print_exception(*sys.exc_info())
其实,下面两句是等价的:
  • traceback.print_exc()
  • traceback.print_exception(*sys.exc_info())

 
traceback 提供的参数可以将 print 的内容写入到文件中
import traceback
try:
a=b
b=c
except:
f=open("log.txt",'a')
traceback.print_exc(file=f)
f.flush()
f.close()
参考:
https://docs.python.org/3/library/traceback.html?highlight=print_tb#traceback.print_exc
https://docs.python.org/3/tutorial/errors.html

Python的生成器和迭代器

采菊篱下 发表了文章 • 0 个评论 • 431 次浏览 • 2016-08-19 18:39 • 来自相关话题

生成器

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
 
 
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
 
 
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:>>> L = [x * x for x in range(5)]
>>> L
[0, 1, 4, 9, 16]
>>> g = (x * x for x in range(5))
>>> g
<generator object <genexpr> at 0x10fe179e8>创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。

我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?
>>> for i in g:
... print(i)
...
0
1
4
9
16
>>> for i in L:
... print(i)
...
0
1
4
9
16
如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:
>>> g = (x * x for x in range(5))
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

# 取完之后就会报错,因为取完了,就没有,异常关键字:StopIterationgenerator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

generator也是可迭代对象,所以,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。
 
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'注意,赋值语句:a, b = b, a + b相当于:
t = (b, a + b) # t是一个tuple
a = t[0]
b = t[1]但不必显式写出临时变量t就可以赋值。

上面的函数可以输出斐波那契数列的前N个数:
>>> fib(10)
1
1
2
3
5
8
13
21
34
55
done仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:
def fib(max):
n,a,b = 0,0,1

while n < max:
#print(b)
yield b
a,b = b,a+b

n += 1

return 'done' 这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:
>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
data = fib(10)
print(data)

print(data.__next__())
print(data.__next__())
print("干点别的事")
print(data.__next__())
print(data.__next__())
print(data.__next__())
print(data.__next__())
print(data.__next__())

#输出
<generator object fib at 0x101be02b0>
1
1
干点别的事
2
3
5
8
13在上面fib的例子,我们在循环过程中不断调用yield,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。

同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:
 
>>> for n in fib(6):
... print(n)
...
1
1
2
3
5
8但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
>>> g = fib(6)
>>> while True:
... try:
... x = next(g)
... print('g:', x)
... except StopIteration as e:
... print('Generator return value:', e.value)
... break
...
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done关于如何捕获错误,后面的错误处理还会详细讲解。

还可通过yield实现在单线程的情况下实现并发运算的效果
import time
def consumer(name):
print("%s 准备吃包子啦!" %name)
while True:
baozi = yield

print("包子[%s]来了,被[%s]吃了!" %(baozi,name))


def producer(name):
c = consumer('A')
c2 = consumer('B')
c.__next__()
c2.__next__()
print("开始准备做包子啦!")
for i in range(10):
time.sleep(1)
print("做了2个包子!")
c.send(i)
c2.send(i)

producer("lucky")

迭代器

我们已经知道,可以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;一类是generator,包括生成器和带yield的generator function。这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。

可以使用isinstance()判断一个对象是否是Iterable对象:
 
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
 
*可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用isinstance()判断一个对象是否是Iterator对象:
>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。

把list、dict、str等Iterable变成Iterator可以使用iter()函数:
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True你可能会问,为什么list、dict、str等数据类型不是Iterator?

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
 
小结
凡是可作用于for循环的对象都是Iterable类型;

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

Python的for循环本质上就是通过不断调用next()函数实现的,例如:
for x in [1, 2, 3, 4, 5]:
pass实际上完全等价于:
# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break 查看全部


生成器


通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
 
 
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
 
 
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
>>> L = [x * x for x in range(5)]
>>> L
[0, 1, 4, 9, 16]
>>> g = (x * x for x in range(5))
>>> g
<generator object <genexpr> at 0x10fe179e8>
创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。

我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?
>>> for i in g:
... print(i)
...
0
1
4
9
16
>>> for i in L:
... print(i)
...
0
1
4
9
16

如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:
>>> g = (x * x for x in range(5))
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

# 取完之后就会报错,因为取完了,就没有,异常关键字:StopIteration
generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

generator也是可迭代对象,所以,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。
 
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
print(b)
a, b = b, a + b
n = n + 1
return 'done'
注意,赋值语句:
a, b = b, a + b
相当于:
t = (b, a + b) # t是一个tuple
a = t[0]
b = t[1]
但不必显式写出临时变量t就可以赋值。

上面的函数可以输出斐波那契数列的前N个数:
>>> fib(10)
1
1
2
3
5
8
13
21
34
55
done
仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:
def fib(max):
n,a,b = 0,0,1

while n < max:
#print(b)
yield b
a,b = b,a+b

n += 1

return 'done'
这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:
>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>
这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
data = fib(10)
print(data)

print(data.__next__())
print(data.__next__())
print("干点别的事")
print(data.__next__())
print(data.__next__())
print(data.__next__())
print(data.__next__())
print(data.__next__())

#输出
<generator object fib at 0x101be02b0>
1
1
干点别的事
2
3
5
8
13
在上面fib的例子,我们在循环过程中不断调用yield,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。

同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:
 
>>> for n in fib(6):
... print(n)
...
1
1
2
3
5
8
但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
>>> g = fib(6)
>>> while True:
... try:
... x = next(g)
... print('g:', x)
... except StopIteration as e:
... print('Generator return value:', e.value)
... break
...
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done
关于如何捕获错误,后面的错误处理还会详细讲解。

还可通过yield实现在单线程的情况下实现并发运算的效果
import time
def consumer(name):
print("%s 准备吃包子啦!" %name)
while True:
baozi = yield

print("包子[%s]来了,被[%s]吃了!" %(baozi,name))


def producer(name):
c = consumer('A')
c2 = consumer('B')
c.__next__()
c2.__next__()
print("开始准备做包子啦!")
for i in range(10):
time.sleep(1)
print("做了2个包子!")
c.send(i)
c2.send(i)

producer("lucky")


迭代器


我们已经知道,可以直接作用于for循环的数据类型有以下几种:
  • 一类是集合数据类型,如list、tuple、dict、set、str等;
  • 一类是generator,包括生成器和带yield的generator function。
  • 这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。


可以使用isinstance()判断一个对象是否是Iterable对象:
 
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
 
*可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用isinstance()判断一个对象是否是Iterator对象:
>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。

把list、dict、str等Iterable变成Iterator可以使用iter()函数:
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
你可能会问,为什么list、dict、str等数据类型不是Iterator?

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
 
小结
凡是可作用于for循环的对象都是Iterable类型;

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

Python的for循环本质上就是通过不断调用next()函数实现的,例如:
for x in [1, 2, 3, 4, 5]:
pass
实际上完全等价于:
# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break

Python3内置函数介绍

采菊篱下 发表了文章 • 0 个评论 • 522 次浏览 • 2016-08-19 17:28 • 来自相关话题

详情参考:https://docs.python.org/3/library/functions.html​ 
 
1、abs(x)(返回一个数的绝对值)
>>> abs(-12)
12
>>> abs(-12.89)
12.89
>>> abs(-0.1)
0.1 
2、all(iterable) 
all会循环括号内的每一个元素,如果括号内的所有元素都是真的,则返回True,如果有一个为假的那么就返回False
>>> all([0, 3])
False
>>> all([1, 3])
True
>>> all([1, ""])
False一假则假,假的参数有:False、0、None、""、[]、()、{}等。
查看一个元素是否为假可以使用bool进行查看:
>>> bool("")
False
>>> bool(())
False
>>> bool(0)
False
>>> bool(1)
True
3、any(iterable)
循环元素,如果有一个元素为真,那么就返回真,一真则真。
>>> any([0, 1, 2, 3])
True
>>> any([{}, (), 0])
False
4、ascii(object)
在对象的类中寻找__repr__方法,获取返回值
 
 
>>> class Foo:
... def __repr_(self):
... return "Result"
...
>>> obj = Foo()
>>> r = ascii(obj)
>>> print(r)
<__main__.Foo object at 0x1045075c0>
5、bin(x)
将整数x转换为二进制字符串,如果x不为Python中int类型,x必须包含方法index()并且返回值为integer
 
# 返回一个整数的二进制
>>> bin(999)
'0b1111100111'# 非整型的情况,必须包含__index__()方法切返回值为integer的类型
>>> class myType:
... def __index__(self):
... return 35
...
>>> myvar = myType()
>>> bin(myvar)
'0b100011'
6、bool([x])
查看一个元素的布尔值,非真即假
>>> bool([])
False
>>> bool(0)
False
>>> bool(1)
True
7、bytearray([source [, encoding [, errors]]])
bytearray([source [, encoding [, errors]]])返回一个byte数组。Bytearray类型是一个可变的序列,并且序列中的元素的取值范围为 [0 ,255]。
 
source参数:
如果source为整数,则返回一个长度为source的初始化数组;如果source为字符串,则按照指定的encoding将字符串转换为字节序列;如果source为可迭代类型,则元素必须为[0 ,255]中的整数;如果source为与buffer接口一致的对象,则此对象也可以被用于初始化bytearray。
 
>>> bytearray(3)
bytearray(b'\x00\x00\x00')
8、bytes([source[, encoding[, errors]]])
>>> bytes("asdasd",encoding="utf-8")
b'asdasd'返回一个bytes类型。
 
9、callable(object)
返回一个对象是否可以被执行
 
>>> def func():
... return 123
...
>>> callable(func)
True
>>> func = 123
>>> callable(func)
False
10、chr(i)
返回一个数字在ASCII编码中对应的字符,取值范围256个
>>> chr(66)
'B'
>>> chr(5)
'\x05'
>>> chr(55)
'7'
>>> chr(255)
'\xff'
>>> chr(25)
'\x19'
>>> chr(65)
'A'
11、compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)
把字符串编译成python可执行的代码
 
>>> str = "for i in range(0,6): print(i)"
>>> c = compile(str, '', 'exec')
>>> exec(c)
0
1
2
3
4
5
 
12、complex([real[, imag]])
创建一个值为real + imag * j的复数或者转化一个字符串或数为复数。如果第一个参数为字符串,则不需要指定第二个参数
>>> complex(1, 2)
(1+2j)
# 数字
>>> complex(1)
(1+0j)
# 当做字符串处理
>>> complex("1")
(1+0j)
# 注意:这个地方在“+”号两边不能有空格,也就是不能写成"1 + 2j",应该是"1+2j",否则会报错
>>> complex("1+2j")
(1+2j)
13、dict(**kwarg)
创建一个数据类型为字典
 
>>> dic = dict({"k1":"123","k2":"456"})
>>> dic
{'k1': '123', 'k2': '456'}
14、dir([object])
返回一个类中的所有方法
 
>>> a = [1, 2, 3, 4]
>>> dir(a)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__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']
15、divmod(a, b)
divmod(a,b)方法返回的是a//b(除法取整)以及a对b的余数,返回结果类型为tuple
 
 
>>> divmod(10, 3)
(3, 1)
16、enumerate(iterable, start=0)
遍历可迭代的数据类型,为元素生成下标
>>> a = [0, 1, 2, 3]
>>> for n, k in enumerate(a):
... print(n, k)
...
0 0
1 1
2 2
3 3
17、eval(expression, globals=None, locals=None)
把一个字符串当作一个表达式去执行
>>> string = "1 + 3"
>>> string
'1 + 3'
>>> eval(string)
4

 
18、exec(object[, globals[, locals]])
把字符串当作python代码执行
>>> exec("for n in range(5): print(n)")
0
1
2
3
4
19、filter(function, iterable)
筛选过滤,循环可迭代的对象,把迭代的对象当作函数的参数,如果符合条件就返回True,否则就返回False
>>> def func(x):
... if x == 11 or x == 22:
... return True
...
>>> ret = filter(func,[11,22,33,44])
>>> for n in ret:
... print(n)
...
11
22
20、float([x])
将整数和字符串转换成浮点数
>>> float("124")
124.0
>>> float("123.45")
123.45
>>> float("-123.34")
-123.34

 
21、format(value[, format_spec]) 
字符串格式化
>>> a = "My name is {0}, age is {1}".format('lucky', 18)
>>> print(a)
My name is lucky, age is 18
22、frozenset([iterable])
frozenset是冻结的集合,它是不可变的,存在哈希值,好处是它可以作为字典的key,也可以作为其它集合的元素。缺点是一旦创建便不能更改,没有add,remove方法。
 
23、globals()
获取当前scripts文件内的所有全局变量
 
>>> a = 3
>>> bsd = "54asd"
>>> globals()
{'__doc__': None, 'bsd': '54asd', '__package__': None, 'a': 3, '__spec__': None, '__builtins__': <module 'builtins' (built-in)>, '__name__': '__main__', '__loader__': <class '_frozen_importlib.BuiltinImporter'>}
24、hash(object)
返回一个对象的hash值
 
>>> a = "asdadasdwqeq234sdfdf"
>>> hash(a)
5390438057823015497
25、help([object])
查看一个类的所有详细方法
 
>>> help(list)
Help on class list in module __builtin__:

class list(object)
| list() -> new empty list
| list(iterable) -> new list initialized from iterable's items
|
| Methods defined here:
|
| __add__(...)
| x.__add__(y) <==> x+y
|
| __contains__(...)
| x.__contains__(y) <==> y in x
|
| __delitem__(...)
| x.__delitem__(y) <==> del x[y]
|
| __delslice__(...)
| x.__delslice__(i, j) <==> del x[i:j]
|
| Use of negative indices is not supported.
..........
26、hex(x)
获取一个数的十六进制
 
>>> hex(13)
'0xd'
27、id(object)
返回一个对象的内存地址
>>> a = 123
>>> id(a)
1835400816
28、input([prompt])
交互式输入
 
 
29、int(x, base=10)
获取一个数的十进制
>>> int("31")
31
30、isinstance(object, classinfo)
判断对象是否是这个类创建的
 
>>> li = [11,22,33]
>>> isinstance(li,list)
True
31、issubclass(class, classinfo)
查看一个对象是否为子类
 
32、iter(object[, sentinel])
创建一个可迭代的对象
>>> obj = iter([11,22,33,44])
>>> obj
<list_iterator object at 0x000002477DB25198>
>>> for n in obj:
... print(n)
...
11
22
33
44

 
33、len(s)
查看一个对象的长度
 
>>> name = 'lucky'
>>> len(name)
5
34、list([iterable])
创建一个数据类型为列表
>>> li = list([11,22,33,44])
>>> li
[11, 22, 33, 44]
35、locals()
返回当前scripts的局部变量,返回结果为字典格式
>>> def func():
... name="lucky"
... print(locals())
...
>>> func()
{'name': 'lucky'}
 
 
36、map(function, iterable, …)
把可迭代的对象作为函数的值
 
>>> ret = map(lambda x: x + 100, [1, 2, 3, 4, 5])
>>> for n in ret:
... print(n)
...
101
102
103
104
105
37、max(iterable, *[, key, default]) max(arg1, arg2, *args[, key])
获取一个对象中的最大值
 
>>> li = [1, 3, 5, 9 ,3]
>>> max(li)
9
38、min(iterable, *[, key, default])  min(arg1, arg2, *args[, key])
获取一个对象中的最小值
>>> li = list([11,22,33,44])
>>> li = [11,22,33,44]
>>> min(li)
11
39、next(iterator[, default])
 
每次只拿取可迭代对象的一个元素
 
>>> obj = iter([11,22,33,44])
>>> next(obj)
11
>>> next(obj)
22
>>> next(obj)
33
>>> next(obj)
44
>>> next(obj)
# 如果没有可迭代的元素了就会报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
40、oct(x)
获取一个字符串的八进制
>>> oct(13)
'0o15'
41、open(file, mode=’r’, buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
文件操作的函数,用来做文件操作的
 
 
 
42、ord(c)
把一个字母转换为ASCII对对应表中的数字
 
>>> ord("a")
97
>>> ord("t")
116
43、pow(x, y[, z])
返回一个数的N次方
>>> pow(2, 10)
1024
>>> pow(2, 20)
1048576
 
44、print(*objects, sep=’ ‘, end=’\n’, file=sys.stdout, flush=False)
打印输出
 
 
45、range(start, stop[, step])
生成一个可迭代序列
 
>>> range(10)
range(0, 10)
>>> for n in range(5):
... print(n)
...
0
1
2
3
4
46、reversed(seq)
对一个对象的元素进行反转
>>> li = [1, 2, 3, 4]
>>> reversed(li)
<list_reverseiterator object at 0x000002CF0EF6A940>
>>> for n in reversed(li):
... print(n)
...
4
3
2
1
47、round(number[, ndigits])
四舍五入
>>> round(3.3)
3
>>> round(3.7)
4
48、set([iterable])
创建一个数据类型为集合
>>> varss = set([11,222,333])
>>> type(varss)
<class 'set'>
49、slice(start, stop[, step])
元素的切片操作都是调用的这个方法
 
50、sorted(iterable[, key][, reverse])
为一个对象的元素进行排序
 
>>> li = [ 2, 3, 4, 9, 1, 100]
>>> sorted(li)
[1, 2, 3, 4, 9, 100]
51、sum(iterable[, start])
求和
>>> sum([11,22,33])
66
52、type(object)
查看一个对象的数据类型
 
>>> a = 1
>>> type(a)
<class 'int'>
>>> a = "str"
>>> type(a)
<class 'str'>
53、vars([object])
查看一个对象里面有多少个变量
 
54、zip(*iterables)
联合迭代
>>> li1 = ["k1","k2","k3"]
>>> li2 = ["a","b","c"]
>>> zip(li1,li2)
<zip object at 0x0000026BF1803288>
>>> dic = zip(li1,li2)
>>> for n in dic:
... print(n)
...
('k1', 'a')
('k2', 'b')
('k3', 'c')
55、import(name, globals=None, locals=None, fromlist=(), level=0)
导入模块,把导入的模块作为一个别名。
生成一个六位的随机验证码,且包含数字,数字的位置随机:
# 导入random模块
import random
temp = ""
for i in range(6):
num = random.randrange(0,4)
if num == 3 or num == 1:
rad2 = random.randrange(0,10)
temp = temp + str(rad2)
else:
rad1 = random.randrange(65,91)
c1 = chr(rad1)
temp = temp + c1
print(temp) 查看全部
intfunc.png

详情参考:https://docs.python.org/3/library/functions.html​ 
 
1、abs(x)(返回一个数的绝对值)
>>> abs(-12)
12
>>> abs(-12.89)
12.89
>>> abs(-0.1)
0.1
 
2、all(iterable) 
all会循环括号内的每一个元素,如果括号内的所有元素都是真的,则返回True,如果有一个为假的那么就返回False
>>> all([0, 3])
False
>>> all([1, 3])
True
>>> all([1, ""])
False
一假则假,假的参数有:False、0、None、""、[]、()、{}等。
查看一个元素是否为假可以使用bool进行查看:
>>> bool("")
False
>>> bool(())
False
>>> bool(0)
False
>>> bool(1)
True

3、any(iterable)
循环元素,如果有一个元素为真,那么就返回真,一真则真。
>>> any([0, 1, 2, 3])
True
>>> any([{}, (), 0])
False

4、ascii(object)
在对象的类中寻找__repr__方法,获取返回值
 
 
>>> class Foo:
... def __repr_(self):
... return "Result"
...
>>> obj = Foo()
>>> r = ascii(obj)
>>> print(r)
<__main__.Foo object at 0x1045075c0>

5、bin(x)
将整数x转换为二进制字符串,如果x不为Python中int类型,x必须包含方法index()并且返回值为integer
 
# 返回一个整数的二进制
>>> bin(999)
'0b1111100111'
# 非整型的情况,必须包含__index__()方法切返回值为integer的类型
>>> class myType:
... def __index__(self):
... return 35
...
>>> myvar = myType()
>>> bin(myvar)
'0b100011'

6、bool([x])
查看一个元素的布尔值,非真即假
>>> bool([])
False
>>> bool(0)
False
>>> bool(1)
True

7、bytearray([source [, encoding [, errors]]])
bytearray([source [, encoding [, errors]]])返回一个byte数组。Bytearray类型是一个可变的序列,并且序列中的元素的取值范围为 [0 ,255]。
 
source参数:
  • 如果source为整数,则返回一个长度为source的初始化数组;
  • 如果source为字符串,则按照指定的encoding将字符串转换为字节序列;
  • 如果source为可迭代类型,则元素必须为[0 ,255]中的整数;
  • 如果source为与buffer接口一致的对象,则此对象也可以被用于初始化bytearray。

 
>>> bytearray(3)
bytearray(b'\x00\x00\x00')

8、bytes([source[, encoding[, errors]]])
>>> bytes("asdasd",encoding="utf-8")
b'asdasd'
返回一个bytes类型。
 
9、callable(object)
返回一个对象是否可以被执行
 
>>> def func():
... return 123
...
>>> callable(func)
True
>>> func = 123
>>> callable(func)
False

10、chr(i)
返回一个数字在ASCII编码中对应的字符,取值范围256个
>>> chr(66)
'B'
>>> chr(5)
'\x05'
>>> chr(55)
'7'
>>> chr(255)
'\xff'
>>> chr(25)
'\x19'
>>> chr(65)
'A'

11、compile(source, filename, mode, flags=0, dont_inherit=False, optimize=-1)
把字符串编译成python可执行的代码
 
>>> str = "for i in range(0,6): print(i)"
>>> c = compile(str, '', 'exec')
>>> exec(c)
0
1
2
3
4
5

 
12、complex([real[, imag]])
创建一个值为real + imag * j的复数或者转化一个字符串或数为复数。如果第一个参数为字符串,则不需要指定第二个参数
>>> complex(1, 2)
(1+2j)
# 数字
>>> complex(1)
(1+0j)
# 当做字符串处理
>>> complex("1")
(1+0j)
# 注意:这个地方在“+”号两边不能有空格,也就是不能写成"1 + 2j",应该是"1+2j",否则会报错
>>> complex("1+2j")
(1+2j)

13、dict(**kwarg)
创建一个数据类型为字典
 
>>> dic = dict({"k1":"123","k2":"456"})
>>> dic
{'k1': '123', 'k2': '456'}

14、dir([object])
返回一个类中的所有方法
 
>>> a = [1, 2, 3, 4]
>>> dir(a)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__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']

15、divmod(a, b)
divmod(a,b)方法返回的是a//b(除法取整)以及a对b的余数,返回结果类型为tuple
 
 
>>> divmod(10, 3)
(3, 1)

16、enumerate(iterable, start=0)
遍历可迭代的数据类型,为元素生成下标
>>> a = [0, 1, 2, 3]
>>> for n, k in enumerate(a):
... print(n, k)
...
0 0
1 1
2 2
3 3

17、eval(expression, globals=None, locals=None)
把一个字符串当作一个表达式去执行
>>> string = "1 + 3"
>>> string
'1 + 3'
>>> eval(string)
4

 
18、exec(object[, globals[, locals]])
把字符串当作python代码执行
>>> exec("for n in range(5): print(n)")
0
1
2
3
4

19、filter(function, iterable)
筛选过滤,循环可迭代的对象,把迭代的对象当作函数的参数,如果符合条件就返回True,否则就返回False
>>> def func(x):
... if x == 11 or x == 22:
... return True
...
>>> ret = filter(func,[11,22,33,44])
>>> for n in ret:
... print(n)
...
11
22

20、float([x])
将整数和字符串转换成浮点数
>>> float("124")
124.0
>>> float("123.45")
123.45
>>> float("-123.34")
-123.34

 
21、format(value[, format_spec]) 
字符串格式化
>>> a = "My name is {0}, age is {1}".format('lucky', 18)
>>> print(a)
My name is lucky, age is 18

22、frozenset([iterable])
frozenset是冻结的集合,它是不可变的,存在哈希值,好处是它可以作为字典的key,也可以作为其它集合的元素。缺点是一旦创建便不能更改,没有add,remove方法。
 
23、globals()
获取当前scripts文件内的所有全局变量
 
>>> a = 3
>>> bsd = "54asd"
>>> globals()
{'__doc__': None, 'bsd': '54asd', '__package__': None, 'a': 3, '__spec__': None, '__builtins__': <module 'builtins' (built-in)>, '__name__': '__main__', '__loader__': <class '_frozen_importlib.BuiltinImporter'>}

24、hash(object)
返回一个对象的hash值
 
>>> a = "asdadasdwqeq234sdfdf"
>>> hash(a)
5390438057823015497

25、help([object])
查看一个类的所有详细方法
 
>>> help(list)
Help on class list in module __builtin__:

class list(object)
| list() -> new empty list
| list(iterable) -> new list initialized from iterable's items
|
| Methods defined here:
|
| __add__(...)
| x.__add__(y) <==> x+y
|
| __contains__(...)
| x.__contains__(y) <==> y in x
|
| __delitem__(...)
| x.__delitem__(y) <==> del x[y]
|
| __delslice__(...)
| x.__delslice__(i, j) <==> del x[i:j]
|
| Use of negative indices is not supported.
..........

26、hex(x)
获取一个数的十六进制
 
>>> hex(13)
'0xd'

27、id(object)
返回一个对象的内存地址
>>> a = 123
>>> id(a)
1835400816

28、input([prompt])
交互式输入
 
 
29、int(x, base=10)
获取一个数的十进制
>>> int("31")
31

30、isinstance(object, classinfo)
判断对象是否是这个类创建的
 
>>> li = [11,22,33]
>>> isinstance(li,list)
True

31、issubclass(class, classinfo)
查看一个对象是否为子类
 
32、iter(object[, sentinel])
创建一个可迭代的对象
>>> obj = iter([11,22,33,44])
>>> obj
<list_iterator object at 0x000002477DB25198>
>>> for n in obj:
... print(n)
...
11
22
33
44

 
33、len(s)
查看一个对象的长度
 
>>> name = 'lucky'
>>> len(name)
5

34、list([iterable])
创建一个数据类型为列表
>>> li = list([11,22,33,44])
>>> li
[11, 22, 33, 44]

35、locals()
返回当前scripts的局部变量,返回结果为字典格式
>>> def func():
... name="lucky"
... print(locals())
...
>>> func()
{'name': 'lucky'}

 
 
36、map(function, iterable, …)
把可迭代的对象作为函数的值
 
>>> ret = map(lambda x: x + 100, [1, 2, 3, 4, 5])
>>> for n in ret:
... print(n)
...
101
102
103
104
105

37、max(iterable, *[, key, default]) max(arg1, arg2, *args[, key])
获取一个对象中的最大值
 
>>> li = [1, 3, 5, 9 ,3]
>>> max(li)
9

38、min(iterable, *[, key, default])  min(arg1, arg2, *args[, key])
获取一个对象中的最小值
>>> li = list([11,22,33,44])
>>> li = [11,22,33,44]
>>> min(li)
11

39、next(iterator[, default])
 
每次只拿取可迭代对象的一个元素
 
>>> obj = iter([11,22,33,44])
>>> next(obj)
11
>>> next(obj)
22
>>> next(obj)
33
>>> next(obj)
44
>>> next(obj)
# 如果没有可迭代的元素了就会报错
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

40、oct(x)
获取一个字符串的八进制
>>> oct(13)
'0o15'

41、open(file, mode=’r’, buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
文件操作的函数,用来做文件操作的
 
 
 
42、ord(c)
把一个字母转换为ASCII对对应表中的数字
 
>>> ord("a")
97
>>> ord("t")
116

43、pow(x, y[, z])
返回一个数的N次方
>>> pow(2, 10)
1024
>>> pow(2, 20)
1048576

 
44、print(*objects, sep=’ ‘, end=’\n’, file=sys.stdout, flush=False)
打印输出
 
 
45、range(start, stop[, step])
生成一个可迭代序列
 
>>> range(10)
range(0, 10)
>>> for n in range(5):
... print(n)
...
0
1
2
3
4

46、reversed(seq)
对一个对象的元素进行反转
>>> li = [1, 2, 3, 4]
>>> reversed(li)
<list_reverseiterator object at 0x000002CF0EF6A940>
>>> for n in reversed(li):
... print(n)
...
4
3
2
1

47、round(number[, ndigits])
四舍五入
>>> round(3.3)
3
>>> round(3.7)
4

48、set([iterable])
创建一个数据类型为集合
>>> varss = set([11,222,333])
>>> type(varss)
<class 'set'>

49、slice(start, stop[, step])
元素的切片操作都是调用的这个方法
 
50、sorted(iterable[, key][, reverse])
为一个对象的元素进行排序
 
>>> li = [ 2, 3, 4, 9, 1, 100]
>>> sorted(li)
[1, 2, 3, 4, 9, 100]

51、sum(iterable[, start])
求和
>>> sum([11,22,33])
66

52、type(object)
查看一个对象的数据类型
 
>>> a = 1
>>> type(a)
<class 'int'>
>>> a = "str"
>>> type(a)
<class 'str'>

53、vars([object])
查看一个对象里面有多少个变量
 
54、zip(*iterables)
联合迭代
>>> li1 = ["k1","k2","k3"]
>>> li2 = ["a","b","c"]
>>> zip(li1,li2)
<zip object at 0x0000026BF1803288>
>>> dic = zip(li1,li2)
>>> for n in dic:
... print(n)
...
('k1', 'a')
('k2', 'b')
('k3', 'c')

55、import(name, globals=None, locals=None, fromlist=(), level=0)
导入模块,把导入的模块作为一个别名。
生成一个六位的随机验证码,且包含数字,数字的位置随机:
# 导入random模块
import random
temp = ""
for i in range(6):
num = random.randrange(0,4)
if num == 3 or num == 1:
rad2 = random.randrange(0,10)
temp = temp + str(rad2)
else:
rad1 = random.randrange(65,91)
c1 = chr(rad1)
temp = temp + c1
print(temp)

服务器主机信息收集

Not see︶ 发表了文章 • 0 个评论 • 533 次浏览 • 2016-08-19 17:24 • 来自相关话题

收集服务器主机信息目的,方便一目了然的去查看,执行dmidecode 感觉比较乱。下面程序可以通过split函数等切片方式进行编写。
目前我想了解的信息是:
1、IP地址(vender)
2、服务器厂商(vender)
3、服务器型号(produ)
4、sn服务器序列号,sn我想显示10个字符
以下程序进行显示:
#!/usr/bin/python

from subprocess import Popen, PIPE

def getIfconfig():
p = Popen(['ifconfig'], stdout=PIPE)
data = p.stdout.read()
return data

def getDmi():
p = Popen(['dmidecode'], stdout=PIPE)
data = p.stdout.read()
return data

def parseData(data):
parsed_data = []
new_line = ''
data = [i for i in data.split('\n') if i]
for line in data:
if line[0].strip():
parsed_data.append(new_line)
new_line = line+'\n'
else:
new_line += line+'\n'
parsed_data.append(new_line)
return [i for i in parsed_data if i]

def parseIfconfig(parsed_data):
dic = {}
parsed_data = [i for i in parsed_data if not i.startswith('lo')]
for lines in parsed_data:
line_list = lines.split('\n')
devname = line_list[0].split()[0]
macaddr = line_list[0].split()[-1]
ipaddr = line_list[1].split()[1].split(':')[1]
break
dic['ip'] = ipaddr
return dic

def parseDmi(parsed_data):
dic = {}
parsed_data = [i for i in parsed_data if i.startswith('System Information')]
parsed_data = [i for i in parsed_data[0].split('\n')[1:] if i]
dmi_dic = dict([i.strip().split(':') for i in parsed_data])
dic['vender'] = dmi_dic['Manufacturer'].strip()
dic['produ'] = dmi_dic['Product Name'].strip()
dic['sn'] = dmi_dic['Serial Number'].strip()[:10]
return dic


if __name__ == '__main__':
data_ip = getIfconfig()
parsed_data_ip = parseData(data_ip)
print parseIfconfig(parsed_data_ip)
data_dmi = getDmi()
parsed_data_dmi = parseData(data_dmi)
print parseDmi(parsed_data_dmi)
[root@Master day5]# ./collect_info_bak.py
{'ip': '192.168.83.169'}
{'vender': 'VMware, Inc.', 'produ': 'VMware Virtual Platform', 'sn': 'VMware-56 '}
接下来我想收集更多的服务器主机信息:
 
1、主机名:hostname
2、IP地址: ip
3、操作系统版本: osver
4、服务器厂商: vender
5、服务器型号: product
6、服务器序列号: sn
7、CPU型号: cpu_model
8、CPU核数: cpu_num
9、内存大小: memory实现方法如下:
#!/usr/bin/python

from subprocess import Popen, PIPE

def getIfconfig():
p = Popen(['ifconfig'], stdout=PIPE)
data = p.stdout.read()
return data

def getDmi():
p = Popen(['dmidecode'], stdout=PIPE)
data = p.stdout.read()
return data

def parseData(data):
parsed_data = []
new_line = ''
data = [i for i in data.split('\n') if i]
for line in data:
if line[0].strip():
parsed_data.append(new_line)
new_line = line+'\n'
else:
new_line += line+'\n'
parsed_data.append(new_line)
return [i for i in parsed_data if i]

def parseIfconfig(parsed_data):
dic = {}
parsed_data = [i for i in parsed_data if not i.startswith('lo')]
for lines in parsed_data:
line_list = lines.split('\n')
devname = line_list[0].split()[0]
macaddr = line_list[0].split()[-1]
ipaddr = line_list[1].split()[1].split(':')[1]
break
dic['ip'] = ipaddr
return dic

def parseDmi(parsed_data):
dic = {}
parsed_data = [i for i in parsed_data if i.startswith('System Information')]
parsed_data = [i for i in parsed_data[0].split('\n')[1:] if i]
dmi_dic = dict([i.strip().split(':') for i in parsed_data])
dic['vender'] = dmi_dic['Manufacturer'].strip()
dic['produ'] = dmi_dic['Product Name'].strip()
dic['sn'] = dmi_dic['Serial Number'].strip()[:10]
return dic

def getHostname(f):
with open(f) as fd:
for line in fd:
if line.startswith('HOSTNAME'):
hostname = line.split('=')[1].strip()
break
return {'hostname':hostname}

def getOSver(f):
with open(f) as fd:
for line in fd:
osver = line.strip()
break
return {'osver':osver}

def getCpu(f):
num = 0
with open(f) as fd:
for line in fd:
if line.startswith('processor'):
num += 1
if line.startswith('model name'):
# print line
cpu_model = line.split(':')[1].split()
cpu_model = cpu_model[0]+' '+cpu_model[-1]
return {'cpu_num':num, 'cpu_model':cpu_model}

def getMemory(f):
with open(f) as fd:
for line in fd:
if line.startswith('MemTotal'):
mem = int(line.split()[1].strip())
break
mem = "%s" % int(mem/1024.0)+'M'
return {'memory':mem}


if __name__ == '__main__':
dic = {}
data_ip = getIfconfig()
parsed_data_ip = parseData(data_ip)
ip = parseIfconfig(parsed_data_ip)
data_dmi = getDmi()
parsed_data_dmi = parseData(data_dmi)
dmi = parseDmi(parsed_data_dmi)
hostname = getHostname('/etc/sysconfig/network')
osver = getOSver('/etc/issue')
cpu = getCpu('/proc/cpuinfo')
mem = getMemory('/proc/meminfo')
dic.update(ip)
dic.update(dmi)
dic.update(hostname)
dic.update(osver)
dic.update(cpu)
dic.update(mem)
print dic这样以后整理资产的时候可以清楚的了解到每台机器的情况了 查看全部
收集服务器主机信息目的,方便一目了然的去查看,执行dmidecode 感觉比较乱。下面程序可以通过split函数等切片方式进行编写。
目前我想了解的信息是:
1、IP地址(vender)
2、服务器厂商(vender)
3、服务器型号(produ)
4、sn服务器序列号,sn我想显示10个字符
以下程序进行显示:
#!/usr/bin/python

from subprocess import Popen, PIPE

def getIfconfig():
p = Popen(['ifconfig'], stdout=PIPE)
data = p.stdout.read()
return data

def getDmi():
p = Popen(['dmidecode'], stdout=PIPE)
data = p.stdout.read()
return data

def parseData(data):
parsed_data = []
new_line = ''
data = [i for i in data.split('\n') if i]
for line in data:
if line[0].strip():
parsed_data.append(new_line)
new_line = line+'\n'
else:
new_line += line+'\n'
parsed_data.append(new_line)
return [i for i in parsed_data if i]

def parseIfconfig(parsed_data):
dic = {}
parsed_data = [i for i in parsed_data if not i.startswith('lo')]
for lines in parsed_data:
line_list = lines.split('\n')
devname = line_list[0].split()[0]
macaddr = line_list[0].split()[-1]
ipaddr = line_list[1].split()[1].split(':')[1]
break
dic['ip'] = ipaddr
return dic

def parseDmi(parsed_data):
dic = {}
parsed_data = [i for i in parsed_data if i.startswith('System Information')]
parsed_data = [i for i in parsed_data[0].split('\n')[1:] if i]
dmi_dic = dict([i.strip().split(':') for i in parsed_data])
dic['vender'] = dmi_dic['Manufacturer'].strip()
dic['produ'] = dmi_dic['Product Name'].strip()
dic['sn'] = dmi_dic['Serial Number'].strip()[:10]
return dic


if __name__ == '__main__':
data_ip = getIfconfig()
parsed_data_ip = parseData(data_ip)
print parseIfconfig(parsed_data_ip)
data_dmi = getDmi()
parsed_data_dmi = parseData(data_dmi)
print parseDmi(parsed_data_dmi)

[root@Master day5]# ./collect_info_bak.py 
{'ip': '192.168.83.169'}
{'vender': 'VMware, Inc.', 'produ': 'VMware Virtual Platform', 'sn': 'VMware-56 '}

接下来我想收集更多的服务器主机信息:
 
1、主机名:hostname
2、IP地址: ip
3、操作系统版本: osver
4、服务器厂商: vender
5、服务器型号: product
6、服务器序列号: sn
7、CPU型号: cpu_model
8、CPU核数: cpu_num
9、内存大小: memory
实现方法如下:
#!/usr/bin/python

from subprocess import Popen, PIPE

def getIfconfig():
p = Popen(['ifconfig'], stdout=PIPE)
data = p.stdout.read()
return data

def getDmi():
p = Popen(['dmidecode'], stdout=PIPE)
data = p.stdout.read()
return data

def parseData(data):
parsed_data = []
new_line = ''
data = [i for i in data.split('\n') if i]
for line in data:
if line[0].strip():
parsed_data.append(new_line)
new_line = line+'\n'
else:
new_line += line+'\n'
parsed_data.append(new_line)
return [i for i in parsed_data if i]

def parseIfconfig(parsed_data):
dic = {}
parsed_data = [i for i in parsed_data if not i.startswith('lo')]
for lines in parsed_data:
line_list = lines.split('\n')
devname = line_list[0].split()[0]
macaddr = line_list[0].split()[-1]
ipaddr = line_list[1].split()[1].split(':')[1]
break
dic['ip'] = ipaddr
return dic

def parseDmi(parsed_data):
dic = {}
parsed_data = [i for i in parsed_data if i.startswith('System Information')]
parsed_data = [i for i in parsed_data[0].split('\n')[1:] if i]
dmi_dic = dict([i.strip().split(':') for i in parsed_data])
dic['vender'] = dmi_dic['Manufacturer'].strip()
dic['produ'] = dmi_dic['Product Name'].strip()
dic['sn'] = dmi_dic['Serial Number'].strip()[:10]
return dic

def getHostname(f):
with open(f) as fd:
for line in fd:
if line.startswith('HOSTNAME'):
hostname = line.split('=')[1].strip()
break
return {'hostname':hostname}

def getOSver(f):
with open(f) as fd:
for line in fd:
osver = line.strip()
break
return {'osver':osver}

def getCpu(f):
num = 0
with open(f) as fd:
for line in fd:
if line.startswith('processor'):
num += 1
if line.startswith('model name'):
# print line
cpu_model = line.split(':')[1].split()
cpu_model = cpu_model[0]+' '+cpu_model[-1]
return {'cpu_num':num, 'cpu_model':cpu_model}

def getMemory(f):
with open(f) as fd:
for line in fd:
if line.startswith('MemTotal'):
mem = int(line.split()[1].strip())
break
mem = "%s" % int(mem/1024.0)+'M'
return {'memory':mem}


if __name__ == '__main__':
dic = {}
data_ip = getIfconfig()
parsed_data_ip = parseData(data_ip)
ip = parseIfconfig(parsed_data_ip)
data_dmi = getDmi()
parsed_data_dmi = parseData(data_dmi)
dmi = parseDmi(parsed_data_dmi)
hostname = getHostname('/etc/sysconfig/network')
osver = getOSver('/etc/issue')
cpu = getCpu('/proc/cpuinfo')
mem = getMemory('/proc/meminfo')
dic.update(ip)
dic.update(dmi)
dic.update(hostname)
dic.update(osver)
dic.update(cpu)
dic.update(mem)
print dic
这样以后整理资产的时候可以清楚的了解到每台机器的情况了

Python装饰器详解

采菊篱下 发表了文章 • 0 个评论 • 553 次浏览 • 2016-08-18 23:11 • 来自相关话题

装饰器

定义:本质就是函数,(装饰其他函数)就是为其他函数添加额外的功能;
遵循的原则:1、不能修改其被装饰函数的源代码    2、不能修改其被装饰函数的调用方式
 
实现装饰器需要用到的知识点:
函数即"变量"高阶函数嵌套函数
如果你对装饰器实现的如上三个知识点理解了的话,那你就完全可以理解装饰器了,图示如下:




下面我们就把装饰器的知识分解一个一个来理解。
 
1、函数即"变量"
让我们来看一下几个例子,来说明函数即变量
函数的错误调用案例,跟变量一样,还没有定义就调用:
# 错误调用方式一:
def foo():
print('in the foo!')
bar()

foo()
# 输出
NameError: name 'bar' is not defined (因为在调用foo函数执行的时候函数体内又调用了bar函数,但是bar函数并没有定义)

# 错误调用方式二:
def foo():
print('in the foo!')
bar()

foo()

def bar():
print('in the bar!')

# 输出
NameError: name 'bar' is not defined (同上面,不同的是虽然你在下面定义了bar函数,但是在调用之后才定义的.)从上面的例子可以看出,函数的定义和调用,其实是跟变量是一样的,变量在没有定义之前你对变量进行操作也是会报变量没有被定义的错误。
 
正确的调用方式如下:
# 错误调用方式二:
def foo():
print('in the foo!')
bar()

def bar():
print('in the bar!')

foo()

# 输出
in the foo!
in the bar!具体说明我们可以结合定义变量图示说明,我们把整个内存看作是一个大楼,把变量看做是门牌号,而变量真正存在的值看做是房间里面的东西,房间就相当于是大内存中的一小块内存块,图示如下:




如图所示我们可以看出函数def下定义的函数名就相当于变量的变量名,而函数体就相当于变量的值;所以函数的执行跟调用变量是一样的,只有在你执行之前所有的函数已经声明,占用了内存地址才可以执行,否则在你执行之前未定义,而会报错。
 
函数和变量一样,如果你已经定义好了,就会从内存中划分出一块内存空间存储进行存储,那什么时候回收这个内存空间呢?1、当你整个程序执行完成后,你所定义的变量或者函数占用的内存空间就会被释放  2、就是你利用del()内置函数的删除方法,把变量名和函数名删除,然后Python的内存回收机制发现这块内存空间没有被引用了,就把这块没有被引用的内存空间回收了。
 
2、高阶函数
高阶函数定义:
把一个函数名当做实参传给另外一个函数当做形参返回值(return)中包含函数名
只要满足以上其中一个条件就称为高阶函数。
 
首先我们看一个高阶函数的例子:
def foo(func):
print('in the foo!')
print(func)
func()


def bar():
print('in the bar!')

foo(bar)

结果:
in the foo!
<function bar at 0x10ba280d0> # 打印了函数bar的内存地址
in the bar!如上是一个符号传递函数名的一个高阶函数的例子。
 
下面我们利用高阶函数来做一个不改变被装饰函数代码,为被装饰函数添加一个新功能,我们这里添加一个程序运行时间的功能。
 
把一个函数名当做实参传给另外一个函数(没有改变函数的代码,添加了一个功能,但是运行方法改变了)
import time
def foo(func):
print('in the foo!')
start_time = time.time()
func()
stop_time = time.time()
print("func run time is %s" % (stop_time - start_time))


def bar():
time.sleep(2)
print('in the bar!')

foo(bar)

# 结果如下:
in the foo!
in the bar!
func run time is 2.002309799194336如上代码把bar函数当做一个实参,传给了foo函数当做形参,然后在里面运行了bar函数。
 
返回值中包含函数(虽然没有改变函数运行方式,但是好像功能没有加上)
import time
def foo(func):
print(func)
return func


def bar():
time.sleep(2)
print('in the bar!')

bar = foo(bar)
bar()

# 结果为:
<function bar at 0x10b0110d0>
in the bar!好像都并没有什么卵用,但是你应该发现了,我可以做到不改变函数调用方式和增加功能,但是单独利用高阶函数,这两个条件同事满足是做不到的,因为还需要结合嵌套函数才行。
 
3、嵌套函数
定义:在一个函数体内创建另外一个函数,这种函数就叫内嵌函数(基于python支持静态嵌套域)
 
函数嵌套示范:def foo():
print('in the foo')
def bar():
print('in the bar')

bar()

foo()这就是一个最简单的一个嵌套函数,一个函数下,再定义另外一个函数。
 
局部作用域和全局作用域的访问顺序:x = 0
def grandpa():
x = 1
def dad():
x = 2
def son():
x = 3
print(x) # 打印出来为3
son()
dad()
grandpa()

print(x) # 这个还是0结果是 3, 0,过程如图所示:




 
闭包:
如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是 closuredef counter(start_num=0):
count=[start_num]
def incr():
count[0]+=1
return count[0]
return incr

print(counter())
print(counter()())
print(counter()())

c=counter()
print(c())
print(c())没看懂,先记录一下。
 
4、装饰器
讲了装饰器 = 高阶函数 + 嵌套函数,还是添加一个打印程序执行时间功能,例子如下:import time
def timer(func):
print("in the timer")
def foo():
start_time = time.time()
func()
stop_time = time.time()
print("fun run time is %s" % (stop_time - start_time))
return foo


def bar():
time.sleep(2)
print('in the bar')

bar = timer(bar)
bar()

# 结果

in the timer
in the bar
fun run time is 2.004901885986328如上代码所示,我们为了给bar函数增加一个计算他运行时间的功能,我们既没有改变他的运行方式bar(),也没有改变他的内部代码,功能,也实现了,这就是利用高阶函数 + 嵌套函数完成的一个简易版的装饰器,在python中为了简化方法,装饰函数可以直接利用@符号把装饰函数应用到被装饰函数的上,如下所示:
import time
def timer(func):
print("in the timer")
def foo():
start_time = time.time()
func()
stop_time = time.time()
print("fun run time is %s" % (stop_time - start_time))
return foo

@timer # @timer 相当于 bar = timer(bar)
def bar():
time.sleep(2)
print('in the bar')

# bar = timer(bar)
bar()

# 结果如下:
in the timer
in the bar
fun run time is 2.000408887863159如上可以看出来,结果是一样的,简易版的装饰器我们可以做出来了,那下面我们在深入一下,做一个高级版本的装饰器,那我们在熟悉如下几个知识点。
 
1、函数参数固定
def decor(func):
def wrapper(n):
print('starting')
func(n)
print('stopping')
return wrapper


def test(n):
print('in the test arg is %s' % n)

decor(test)('lucky')分解过程:




其实就是 decor(test)('lucky')   的过程你可以理解为  a = decor(test) = wrapper  然后在调用  a('lucky') = wrapper('lucky') 。
 
2、函数参数不固定
def decor(func):
def wrapper(*args, **kwargs):
print('starting')
func(*args, **kwargs)
print('stopping')
return wrapper


def test(n, x=1):
print('in the test n is %s, x is %s' % (n, x))

decor(test)('alex', x=2)

# 结果如下:
starting
in the test n is alex, x is 2
stopping
3、无参装饰器
import time
def decorator(func):
def wrapper(*args, **kwargs):
start = time.time()
func(*args, **kwargs)
stop = time.time()
print('run time is %s ' % (stop - start))
print('timeout')
return wrapper

@decorator
def test(list_test):
for i in list_test:
time.sleep(0.1)
print('-' * 20, i)

#decorator(test)(range(10))
test(range(10))
4、带参数的装饰器
import time
def timer(timeout=0):
def decorator(func):
def wrapper(*args, **kwargs):
start=time.time()
func(*args, **kwargs)
stop=time.time()
print('run time is %s ' %(stop-start))
print(timeout)
return wrapper
return decorator

@timer(2)
def test(list_test):
for i in list_test:
time.sleep(0.1)
print('-'*20, i)

# timer(timeout=2)(test)(range(10)) timer(timeout=2) = decorator timer(timeout=2)(test) = decorator(test) = wrapper
# timer(timeout=2)(test)(range(10)) = decorator(test)(range(10)) = wrapper(range(10))
test(range(10))
高级版装饰器:
user, passwd = 'lucky', 'abc123'

def auth(auth_type):
print("auth func:", auth_type)
def outer_wrapper(func):
def wrapper(*args, **kwargs):
print("wrapper func args:", *args, **kwargs)
if auth_type == "local":
username = input("Username:").strip()
password = input("Password:").strip()
if user == username and passwd == password:
print("\033[32mUser has passwed authentication\033[0m")
res = func(*args, **kwargs) # from home
print("---after authenticaion")
return res
else:
exit("\033[31mInvalid username or passwd\033[0m")
elif auth_type == "ldap":
print("\033[33m不会ldap,搞毛线啊!\033[0m")
return wrapper
return outer_wrapper

@auth(auth_type="ldap")
def bbs():
print("welcome to bbs page")

@auth(auth_type="local") # home = wrapper()
def home():
print("welcome to home page")
return "from home"

bbs()
home()
装饰器的应用:
装饰器功能:函数超时则终止
# -*- coding: utf-8 -*-
from threading import Thread
import time

class TimeoutException(Exception):
pass

ThreadStop = Thread._Thread__stop#获取私有函数

def timelimited(timeout):
def decorator(function):
def decorator2(*args,**kwargs):
class TimeLimited(Thread):
def __init__(self,_error= None,):
Thread.__init__(self)
self._error = _error

def run(self):
try:
self.result = function(*args,**kwargs)
except Exception,e:
self._error =e

def _stop(self):
if self.isAlive():
ThreadStop(self)

t = TimeLimited()
t.start()
t.join(timeout)

if isinstance(t._error,TimeoutException):
t._stop()
raise TimeoutException('timeout for %s' % (repr(function)))

if t.isAlive():
t._stop()
raise TimeoutException('timeout for %s' % (repr(function)))

if t._error is None:
return t.result

return decorator2
return decorator

@timelimited(2)
def fn_1(secs):
time.sleep(secs)
return 'Finished'

if __name__ == "__main__":
print fn_1(4) 查看全部


装饰器


定义:本质就是函数,(装饰其他函数)就是为其他函数添加额外的功能;
遵循的原则:1、不能修改其被装饰函数的源代码    2、不能修改其被装饰函数的调用方式
 
实现装饰器需要用到的知识点:
  1. 函数即"变量"
  2. 高阶函数
  3. 嵌套函数

如果你对装饰器实现的如上三个知识点理解了的话,那你就完全可以理解装饰器了,图示如下:
Decorator.png

下面我们就把装饰器的知识分解一个一个来理解。
 
1、函数即"变量"
让我们来看一下几个例子,来说明函数即变量
函数的错误调用案例,跟变量一样,还没有定义就调用:
# 错误调用方式一:
def foo():
print('in the foo!')
bar()

foo()
# 输出
NameError: name 'bar' is not defined (因为在调用foo函数执行的时候函数体内又调用了bar函数,但是bar函数并没有定义)

# 错误调用方式二:
def foo():
print('in the foo!')
bar()

foo()

def bar():
print('in the bar!')

# 输出
NameError: name 'bar' is not defined (同上面,不同的是虽然你在下面定义了bar函数,但是在调用之后才定义的.)
从上面的例子可以看出,函数的定义和调用,其实是跟变量是一样的,变量在没有定义之前你对变量进行操作也是会报变量没有被定义的错误。
 
正确的调用方式如下:
# 错误调用方式二:
def foo():
print('in the foo!')
bar()

def bar():
print('in the bar!')

foo()

# 输出
in the foo!
in the bar!
具体说明我们可以结合定义变量图示说明,我们把整个内存看作是一个大楼,把变量看做是门牌号,而变量真正存在的值看做是房间里面的东西,房间就相当于是大内存中的一小块内存块,图示如下:
funcvar.png

如图所示我们可以看出函数def下定义的函数名就相当于变量的变量名,而函数体就相当于变量的值;所以函数的执行跟调用变量是一样的,只有在你执行之前所有的函数已经声明,占用了内存地址才可以执行,否则在你执行之前未定义,而会报错。
 
函数和变量一样,如果你已经定义好了,就会从内存中划分出一块内存空间存储进行存储,那什么时候回收这个内存空间呢?1、当你整个程序执行完成后,你所定义的变量或者函数占用的内存空间就会被释放  2、就是你利用del()内置函数的删除方法,把变量名和函数名删除,然后Python的内存回收机制发现这块内存空间没有被引用了,就把这块没有被引用的内存空间回收了。
 
2、高阶函数
高阶函数定义:
  1. 把一个函数名当做实参传给另外一个函数当做形参
  2. 返回值(return)中包含函数名

只要满足以上其中一个条件就称为高阶函数。
 
首先我们看一个高阶函数的例子:
def foo(func):
print('in the foo!')
print(func)
func()


def bar():
print('in the bar!')

foo(bar)

结果:
in the foo!
<function bar at 0x10ba280d0> # 打印了函数bar的内存地址
in the bar!
如上是一个符号传递函数名的一个高阶函数的例子。
 
下面我们利用高阶函数来做一个不改变被装饰函数代码,为被装饰函数添加一个新功能,我们这里添加一个程序运行时间的功能。
 
把一个函数名当做实参传给另外一个函数(没有改变函数的代码,添加了一个功能,但是运行方法改变了)
import time
def foo(func):
print('in the foo!')
start_time = time.time()
func()
stop_time = time.time()
print("func run time is %s" % (stop_time - start_time))


def bar():
time.sleep(2)
print('in the bar!')

foo(bar)

# 结果如下:
in the foo!
in the bar!
func run time is 2.002309799194336
如上代码把bar函数当做一个实参,传给了foo函数当做形参,然后在里面运行了bar函数。
 
返回值中包含函数(虽然没有改变函数运行方式,但是好像功能没有加上)
import time
def foo(func):
print(func)
return func


def bar():
time.sleep(2)
print('in the bar!')

bar = foo(bar)
bar()

# 结果为:
<function bar at 0x10b0110d0>
in the bar!
好像都并没有什么卵用,但是你应该发现了,我可以做到不改变函数调用方式和增加功能,但是单独利用高阶函数,这两个条件同事满足是做不到的,因为还需要结合嵌套函数才行。
 
3、嵌套函数
定义:在一个函数体内创建另外一个函数,这种函数就叫内嵌函数(基于python支持静态嵌套域)
 
函数嵌套示范:
def foo():
print('in the foo')
def bar():
print('in the bar')

bar()

foo()
这就是一个最简单的一个嵌套函数,一个函数下,再定义另外一个函数。
 
局部作用域和全局作用域的访问顺序:
x = 0
def grandpa():
x = 1
def dad():
x = 2
def son():
x = 3
print(x) # 打印出来为3
son()
dad()
grandpa()

print(x) # 这个还是0
结果是 3, 0,过程如图所示:
sorted.png

 
闭包:
如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是 closure
def counter(start_num=0):
count=[start_num]
def incr():
count[0]+=1
return count[0]
return incr

print(counter())
print(counter()())
print(counter()())

c=counter()
print(c())
print(c())
没看懂,先记录一下。
 
4、装饰器
讲了装饰器 = 高阶函数 + 嵌套函数,还是添加一个打印程序执行时间功能,例子如下:
import time
def timer(func):
print("in the timer")
def foo():
start_time = time.time()
func()
stop_time = time.time()
print("fun run time is %s" % (stop_time - start_time))
return foo


def bar():
time.sleep(2)
print('in the bar')

bar = timer(bar)
bar()

# 结果

in the timer
in the bar
fun run time is 2.004901885986328
如上代码所示,我们为了给bar函数增加一个计算他运行时间的功能,我们既没有改变他的运行方式bar(),也没有改变他的内部代码,功能,也实现了,这就是利用高阶函数 + 嵌套函数完成的一个简易版的装饰器,在python中为了简化方法,装饰函数可以直接利用@符号把装饰函数应用到被装饰函数的上,如下所示:
import time
def timer(func):
print("in the timer")
def foo():
start_time = time.time()
func()
stop_time = time.time()
print("fun run time is %s" % (stop_time - start_time))
return foo

@timer # @timer 相当于 bar = timer(bar)
def bar():
time.sleep(2)
print('in the bar')

# bar = timer(bar)
bar()

# 结果如下:
in the timer
in the bar
fun run time is 2.000408887863159
如上可以看出来,结果是一样的,简易版的装饰器我们可以做出来了,那下面我们在深入一下,做一个高级版本的装饰器,那我们在熟悉如下几个知识点。
 
1、函数参数固定
def decor(func):
def wrapper(n):
print('starting')
func(n)
print('stopping')
return wrapper


def test(n):
print('in the test arg is %s' % n)

decor(test)('lucky')
分解过程:
argsfun.png

其实就是 decor(test)('lucky')   的过程你可以理解为  a = decor(test) = wrapper  然后在调用  a('lucky') = wrapper('lucky') 。
 
2、函数参数不固定
def decor(func):
def wrapper(*args, **kwargs):
print('starting')
func(*args, **kwargs)
print('stopping')
return wrapper


def test(n, x=1):
print('in the test n is %s, x is %s' % (n, x))

decor(test)('alex', x=2)

# 结果如下:
starting
in the test n is alex, x is 2
stopping

3、无参装饰器
import time
def decorator(func):
def wrapper(*args, **kwargs):
start = time.time()
func(*args, **kwargs)
stop = time.time()
print('run time is %s ' % (stop - start))
print('timeout')
return wrapper

@decorator
def test(list_test):
for i in list_test:
time.sleep(0.1)
print('-' * 20, i)

#decorator(test)(range(10))
test(range(10))

4、带参数的装饰器
import time
def timer(timeout=0):
def decorator(func):
def wrapper(*args, **kwargs):
start=time.time()
func(*args, **kwargs)
stop=time.time()
print('run time is %s ' %(stop-start))
print(timeout)
return wrapper
return decorator

@timer(2)
def test(list_test):
for i in list_test:
time.sleep(0.1)
print('-'*20, i)

# timer(timeout=2)(test)(range(10)) timer(timeout=2) = decorator timer(timeout=2)(test) = decorator(test) = wrapper
# timer(timeout=2)(test)(range(10)) = decorator(test)(range(10)) = wrapper(range(10))
test(range(10))

高级版装饰器:
user, passwd = 'lucky', 'abc123'

def auth(auth_type):
print("auth func:", auth_type)
def outer_wrapper(func):
def wrapper(*args, **kwargs):
print("wrapper func args:", *args, **kwargs)
if auth_type == "local":
username = input("Username:").strip()
password = input("Password:").strip()
if user == username and passwd == password:
print("\033[32mUser has passwed authentication\033[0m")
res = func(*args, **kwargs) # from home
print("---after authenticaion")
return res
else:
exit("\033[31mInvalid username or passwd\033[0m")
elif auth_type == "ldap":
print("\033[33m不会ldap,搞毛线啊!\033[0m")
return wrapper
return outer_wrapper

@auth(auth_type="ldap")
def bbs():
print("welcome to bbs page")

@auth(auth_type="local") # home = wrapper()
def home():
print("welcome to home page")
return "from home"

bbs()
home()

装饰器的应用:
装饰器功能:函数超时则终止
# -*- coding: utf-8 -*-  
from threading import Thread
import time

class TimeoutException(Exception):
pass

ThreadStop = Thread._Thread__stop#获取私有函数

def timelimited(timeout):
def decorator(function):
def decorator2(*args,**kwargs):
class TimeLimited(Thread):
def __init__(self,_error= None,):
Thread.__init__(self)
self._error = _error

def run(self):
try:
self.result = function(*args,**kwargs)
except Exception,e:
self._error =e

def _stop(self):
if self.isAlive():
ThreadStop(self)

t = TimeLimited()
t.start()
t.join(timeout)

if isinstance(t._error,TimeoutException):
t._stop()
raise TimeoutException('timeout for %s' % (repr(function)))

if t.isAlive():
t._stop()
raise TimeoutException('timeout for %s' % (repr(function)))

if t._error is None:
return t.result

return decorator2
return decorator

@timelimited(2)
def fn_1(secs):
time.sleep(secs)
return 'Finished'

if __name__ == "__main__":
print fn_1(4)

Python匿名函数和高阶函数

采菊篱下 发表了文章 • 0 个评论 • 394 次浏览 • 2016-08-17 23:23 • 来自相关话题

匿名函数

Lambda(Lambda expressions)表达式是用lambda关键字创建的匿名函数,Lambda函数可以用于任何需要函数对象的地方,在语法上,它们被局限于只能有一个单独的表达式,但是他可以跟其他内置函数map,reduce,filter等联合使用,更加便利和快捷的一些方法。
 
lambda 和if  else的三元运算一样,是为了简化函数定义,但是:
只能做简单的操作自动return
例子如下:
# 这段代码
def f(x, y):
return x + y
print(f(3, 4))

# 换成匿名函数
f = lambda x, y: x + y

print(f(3, 4))
效果是相同的结果都是7,lambda创建的函数和def创建的函数对应关系如图所示:




 
这看起来并没有什么卵用,但是它和其他内置函数搭配起来,具体联合使用请参考:http://openskill.cn/article/272 

高阶函数

变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
 
满足下列条件之一就可成函数为高阶函数:
某一函数当做实参传入另一个函数中函数的返回值包含n个函数,n>0
 
先来看一个简单的例子:
def bar():
print 'in the bar'


def foo(func):
res=func()
return res

foo(bar)首先定义好函数bar和foo,然后执行foo的时候把bar函数当实参传递进去。 查看全部


匿名函数


Lambda(Lambda expressions)表达式是用lambda关键字创建的匿名函数,Lambda函数可以用于任何需要函数对象的地方,在语法上,它们被局限于只能有一个单独的表达式,但是他可以跟其他内置函数map,reduce,filter等联合使用,更加便利和快捷的一些方法。
 
lambda 和if  else的三元运算一样,是为了简化函数定义,但是:
  1. 只能做简单的操作
  2. 自动return

例子如下:
# 这段代码
def f(x, y):
return x + y
print(f(3, 4))

# 换成匿名函数
f = lambda x, y: x + y

print(f(3, 4))

效果是相同的结果都是7,lambda创建的函数和def创建的函数对应关系如图所示:
lambda.png

 
这看起来并没有什么卵用,但是它和其他内置函数搭配起来,具体联合使用请参考:http://openskill.cn/article/272 


高阶函数


变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
 
满足下列条件之一就可成函数为高阶函数:
  1. 某一函数当做实参传入另一个函数中
  2. 函数的返回值包含n个函数,n>0

 
先来看一个简单的例子:
def bar():
print 'in the bar'


def foo(func):
res=func()
return res

foo(bar)
首先定义好函数bar和foo,然后执行foo的时候把bar函数当实参传递进去。

Python三元计算和深浅拷贝

采菊篱下 发表了文章 • 0 个评论 • 432 次浏览 • 2016-08-17 21:54 • 来自相关话题

三元计算

三元计算也称为三目计算,如名字表示的三元运算符需要三个操作数,语法为:
条件表达式?表达式1:表达式2。 or
var = 值1 if 条件 else 值2
说明:问号前面的位置是判断的条件,判断结果为bool型,为true时调用表达式1,为false时调用表达式2。
其逻辑为:如果为真执行第一个,否则执行第二个。
例子:
>>> a = 'xxoo'

>>> Re = 'xx' if a == 'xxoo' else 'oo'
>>> print(Re)
xx

>>> a = 'xx'
>>> Re = 'xx' if a == 'xxoo' else 'oo'
>>> print(Re)
oo如上结果你可以看成是如下代码的简写的例子:
a = 'xxoo'
if a == 'xxoo':
Re = 'xx'
else:
Re = 'oo'这样我想你就知道了!
 

深浅拷贝

深浅拷贝分为两部分,一部分是数字和字符串另一部分是列表、元组、字典等其他数据类型。
 
数字和字符串:
对于数字和字符串而言,赋值、浅拷贝和深拷贝无意义,因为他们的值永远都会指向同一个内存地址,他们就是内存地址的复用。
>>> import copy # 导入内置copy模块
>>> name = 'lucky' # 命名name变量
>>> id(name) # 查看name变量的内存地址
4426658624
>>>
>>> name1 = name # 赋值应用变量 name1
>>> id(name1) # 查看变量name1内存地址
4426658624
>>>
>>> name2 = copy.copy(name) # 浅copy name变量赋值给name2变量
>>> id(name2) # 查看name2变量内存地址
4426658624
>>>
>>> name3 = copy.deepcopy(name) # 深copy name变量复制给name2变量
>>> id(name3) # 查看name3变量的内存地址
4426658624
>>>
>>> print(name, name1, name2, name3) # 打印四个变量的值
lucky lucky lucky lucky如上结果可以看出,不管是直接引用还是深浅copy其最后的结果变量的内存地址都没有变,只是定义的方法不一样,如下图示:




 
其他数据类型:
对于字典、元祖、列表 而言,进行赋值、浅拷贝和深拷贝时,内存地址可能会发生相应的变化。
1、赋值
>>> num1 = {'n1': 1, 'n2': 2, 'n3': [3, 4]}
>>> num2 = num1
>>> print(id(num1), id(num2))
4424864840 4424864840
>>>
>>> num1['n1'] = 7
>>> print(id(num1), id(num2))
4424864840 4424864840
>>> print(num1, num2)
{'n2': 2, 'n3': [3, 4], 'n1': 7} {'n2': 2, 'n3': [3, 4], 'n1': 7}
>>>
>>> num1['n3'][0] = 9
>>> print(id(num1), id(num2))
4424864840 4424864840
>>> print(num1, num2)
{'n2': 2, 'n3': [9, 4], 'n1': 7} {'n2': 2, 'n3': [9, 4], 'n1': 7}
如上所示,不管怎么改变原始的info字典内容,哪怕是改变里面的列表元素值,而info2的内容随之变化,运维他们的引用的内存地址是一样的,图示如下:




 
2、浅拷贝
 
浅拷贝,在内存中只额外创建第一层数据,里面则是完全复制。
>>> num1 = {'n1': 1, 'n2': 2, 'n3': [3, 4]}
>>> num2 = num1.copy()
>>> print(id(num1), id(num2))
4426663432 4426602824

>>> print(id(num1['n1']), id(num2['n1']))
4423026704 4423026704
>>> print(id(num1['n3']), id(num2['n3']))
4427044168 4427044168
>>> print(id(num1['n3'][0]), id(num2['n3'][0]))
4423026768 4423026768
>>> num1['n3'][0] = 9
>>> print(id(num1), id(num2))
4426663432 4426602824
>>> print(num1, num2)
{'n2': 2, 'n3': [9, 4], 'n1': 1} {'n2': 2, 'n3': [9, 4], 'n1': 1}
>>> num1['n1'] = 19
>>> print(num1, num2)
{'n2': 2, 'n3': [9, 4], 'n1': 19} {'n2': 2, 'n3': [9, 4], 'n1': 1}
>>> print(id(num1['n3'][0]), id(num2['n3'][0]))
4423026960 4423026960
浅copy也可以利用copy模块 num2 = copy.copy(num1)
 图示如下:




 
3、深拷贝
深拷贝,在内存中将所有的数据重新创建一份(排除最后一层,即:python内部对字符串和数字的优化)
 
>>> num1 = {'n1': 1, 'n2': 2, 'n3': [3, 4]}
>>> num2 = copy.deepcopy(num1)
>>> print(id(num1), id(num2))
4424864840 4427043656
>>> print(id(num1['n1']), id(num2['n1']))
4423026704 4423026704
>>> print(id(num1['n3']), id(num2['n3']))
4427043592 4426939976
>>> num1['n1'] = 9
>>> print(id(num1['n1']), id(num2['n1']))
4423026960 4423026704从上面来看,深copy内存地址则完全不一样,图示如下: 查看全部


三元计算


三元计算也称为三目计算,如名字表示的三元运算符需要三个操作数,语法为:
条件表达式?表达式1:表达式2。  or  
var = 值1 if 条件 else 值2
说明:问号前面的位置是判断的条件,判断结果为bool型,为true时调用表达式1,为false时调用表达式2。
其逻辑为:如果为真执行第一个,否则执行第二个。
例子:
>>> a = 'xxoo'

>>> Re = 'xx' if a == 'xxoo' else 'oo'
>>> print(Re)
xx

>>> a = 'xx'
>>> Re = 'xx' if a == 'xxoo' else 'oo'
>>> print(Re)
oo
如上结果你可以看成是如下代码的简写的例子:
a = 'xxoo'
if a == 'xxoo':
Re = 'xx'
else:
Re = 'oo'
这样我想你就知道了!
 


深浅拷贝


深浅拷贝分为两部分,一部分是数字和字符串另一部分是列表、元组、字典等其他数据类型。
 
数字和字符串:
对于数字和字符串而言,赋值、浅拷贝和深拷贝无意义,因为他们的值永远都会指向同一个内存地址,他们就是内存地址的复用。
>>> import copy      # 导入内置copy模块
>>> name = 'lucky' # 命名name变量
>>> id(name) # 查看name变量的内存地址
4426658624
>>>
>>> name1 = name # 赋值应用变量 name1
>>> id(name1) # 查看变量name1内存地址
4426658624
>>>
>>> name2 = copy.copy(name) # 浅copy name变量赋值给name2变量
>>> id(name2) # 查看name2变量内存地址
4426658624
>>>
>>> name3 = copy.deepcopy(name) # 深copy name变量复制给name2变量
>>> id(name3) # 查看name3变量的内存地址
4426658624
>>>
>>> print(name, name1, name2, name3) # 打印四个变量的值
lucky lucky lucky lucky
如上结果可以看出,不管是直接引用还是深浅copy其最后的结果变量的内存地址都没有变,只是定义的方法不一样,如下图示:
strcopy.png

 
其他数据类型:
对于字典、元祖、列表 而言,进行赋值、浅拷贝和深拷贝时,内存地址可能会发生相应的变化。
1、赋值
>>> num1 = {'n1': 1, 'n2': 2, 'n3': [3, 4]}
>>> num2 = num1
>>> print(id(num1), id(num2))
4424864840 4424864840
>>>
>>> num1['n1'] = 7
>>> print(id(num1), id(num2))
4424864840 4424864840
>>> print(num1, num2)
{'n2': 2, 'n3': [3, 4], 'n1': 7} {'n2': 2, 'n3': [3, 4], 'n1': 7}
>>>
>>> num1['n3'][0] = 9
>>> print(id(num1), id(num2))
4424864840 4424864840
>>> print(num1, num2)
{'n2': 2, 'n3': [9, 4], 'n1': 7} {'n2': 2, 'n3': [9, 4], 'n1': 7}

如上所示,不管怎么改变原始的info字典内容,哪怕是改变里面的列表元素值,而info2的内容随之变化,运维他们的引用的内存地址是一样的,图示如下:
fuzhi.png

 
2、浅拷贝
 
浅拷贝,在内存中只额外创建第一层数据,里面则是完全复制。
>>> num1 = {'n1': 1, 'n2': 2, 'n3': [3, 4]}
>>> num2 = num1.copy()
>>> print(id(num1), id(num2))
4426663432 4426602824

>>> print(id(num1['n1']), id(num2['n1']))
4423026704 4423026704
>>> print(id(num1['n3']), id(num2['n3']))
4427044168 4427044168
>>> print(id(num1['n3'][0]), id(num2['n3'][0]))
4423026768 4423026768
>>> num1['n3'][0] = 9
>>> print(id(num1), id(num2))
4426663432 4426602824
>>> print(num1, num2)
{'n2': 2, 'n3': [9, 4], 'n1': 1} {'n2': 2, 'n3': [9, 4], 'n1': 1}
>>> num1['n1'] = 19
>>> print(num1, num2)
{'n2': 2, 'n3': [9, 4], 'n1': 19} {'n2': 2, 'n3': [9, 4], 'n1': 1}
>>> print(id(num1['n3'][0]), id(num2['n3'][0]))
4423026960 4423026960

浅copy也可以利用copy模块 num2 = copy.copy(num1)
 图示如下:
ashcopy.png

 
3、深拷贝
深拷贝,在内存中将所有的数据重新创建一份(排除最后一层,即:python内部对字符串和数字的优化)
 
>>> num1 = {'n1': 1, 'n2': 2, 'n3': [3, 4]}
>>> num2 = copy.deepcopy(num1)
>>> print(id(num1), id(num2))
4424864840 4427043656
>>> print(id(num1['n1']), id(num2['n1']))
4423026704 4423026704
>>> print(id(num1['n3']), id(num2['n3']))
4427043592 4426939976
>>> num1['n1'] = 9
>>> print(id(num1['n1']), id(num2['n1']))
4423026960 4423026704
从上面来看,深copy内存地址则完全不一样,图示如下:
deepcopy.png

Python函数详解

采菊篱下 发表了文章 • 2 个评论 • 533 次浏览 • 2016-08-15 00:43 • 来自相关话题

在介绍Python的函数之前我们先来回顾一下,我们平常写代码是不是需要什么功能就写什么功能,但是到后面深入你会发现在写代码的过程中,有好多代码是重叠的,就是一个功能块在不同的地方都被使用,但是每次都得重新写一遍,这样就造成了写程序的效率问题,而Python中的函数就可以很好的解决这么一点。例如一个登陆代码快的代码,你比如刚登陆的时候需要验证用户名密码,你执行他事件比如购物、结算等逻辑的时候也需要做验证,那如果我们把这个验证的逻辑封装成一个函数,然后用到的地方,想怎么调就怎么调用,所以减少代码的重复性是函数的特性之一。
 
同时在过去的十年间,大家广为熟知的编程方法无非两种:面向对象和面向过程,其实,无论哪种,都是一种编程的规范或者是如何编程的方法论。而如今,一种更为古老的编程方式:函数式编程,以其不保存状态,不修改变量等特性重新进入人们的视野。下面我们就来依次了解这一传统的编程理念,下面我们介绍一些Python中函数的概念。
 
面向过程:根据业务逻辑从上到下写垒代码
函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可

函数是什么?

在我们的初中书写中我们应该知道3x  = 4y 这就是一个简单的函数,而x、y就是两个变量,如果x确定为一个值,那y的值就是确定的,我们把x叫做自变量,而把y叫做应变量,y是x的函数,当然也可以反过来说。这里就是y会随着x的值变化而变化。而这个自变量x的取值范围我们就叫做这个函数的定义域。
 
函数一词虽源于数学,但编程中的「函数」概念,与数学中的函数是有很大不同的,具体区别,我们后面会讲,编程中的函数在英文中也有很多不同的叫法。在BASIC中叫做subroutine(子过程或子程序),在Pascal中叫做procedure(过程)和function,在C中只有function,在Java里面叫做method。
 
定义: 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名的方法即可

函数的特性:
减少重复代码使程序变的可扩展(装饰器本质是函数)使程序变得易维护
 
语法定义:def test(x):
"The function definitions"
x+=1
return x

******************************************************************
def:定义函数的关键字
test:函数名
():内可定义形参
"":文档描述(非必要,但是强烈建议为你的函数添加描述信息)
x+=1:泛指代码块或程序处理逻辑
return:定义返回值**编程语言中函数定义:函数是逻辑结构化和过程化的一种编程方法,函数式编程就是先定义一个数学函数,然后按照这个数学模型用编程语言去实现它。
 
一个栗子:#!/usr/bin/env python3
# _*_coding:utf-8_*_
# Author: Lucky.chen

# 常规码代码
a, b = 5, 8
c = a * b
print(c)

print("我是分割线".center(30, '*'))
# 函数方法
def calc(x, y):
res = x * y
return res # 返回函数执行结果

c = calc(a, b) # 结果赋值给c变量
print(c)Result:40
************我是分割线*************
40这不是功能一样吗,看起来并没有什么卵用!!!嘿嘿!

函数参数与局部变量





形参:形式参数,不是实际存在,是虚拟变量。在定义函数和函数体的时候使用形参,目的是在函数调用时接收实参(实参个数,类型应与实参一一对应)。实参:实际参数,调用函数时传给函数的参数,可以是常量,变量,表达式,函数,传给形参。
区别:形参是虚拟的,不占用内存空间,形参变量只有在被调用时才分配内存单元,实参是一个变量,占用内存空间,数据传送单向,实参传给形参,不能形参传给实参。实参就是函数执行的时候用户调用函数实际传输的参数简称为实参,实参又分为位置参数和关键参数。
 
位置参数实例如下:def test(x, y): # x, y是形参
print("x is %d, y is %d" % (x, y))
calc_age = x * y
return calc_age

# 3在前,6在后 那么传给函数的形参就是按照位置 x = 3 , y = 6
print(test(3, 6))
print("我是分割线".center(30, '*'))
# 如果调用的时候6在前,3在后,那么 x = 6, y = 3
print(test(6, 3))结果如下:x is 3, y is 6
18
************我是分割线*************
x is 6, y is 3
18关键参数实例如下:def test(x, y, z): # x, y是形参
print("x is %d, y is %d, z is %d" % (x, y, z))
calc_age = x * y * z
return calc_age

# 指定y和z的值而x的值就是默认出入位置为第个的实参
print(test(3, y=2, z=3))
print("我是分割线".center(30, '*'))
# 指定y和z的值,但是y和z的位置对调
print(test(3, z=2, y=3))结果如下:x is 3, y is 2, z is 3
18
************我是分割线*************
x is 3, y is 3, z is 2
18如果这样呢?def test(x, y, z): # x, y是形参
print("x is %d, y is %d, z is %d" % (x, y, z))
calc_age = x * y * z
return calc_age

# 我指定第一个实参的数值,而不指定y和z的呢?
print(test(x=3, 2, 3))结果如下报错了: File "/Users/crh/PycharmProjects/app/test.py", line 12
print(test(x=3, 2, 3))
^
SyntaxError: positional argument follows keyword argument如上实例介绍可以得出如下结论:
正常情况下,给函数传参数要按顺序,不想按顺序就可以用关键参数,只需指定参数名即可,但记住一个要求就是,关键参数必须放在位置参数之后。
 
默认参数实例如下:def test(x, y=3, z): # x, y是形参
print("x is %d, y is %d, z is %d" % (x, y, z))
calc_age = x * y * z
return calc_age

# 只传两个参数,按照正常逻辑 x=1, y=3, z=3
print(test(1, 3))
# 但是报错了报错如下: File "/Users/crh/PycharmProjects/app/test.py", line 6
def test(x, y=3, z): # x, y是形参
^
SyntaxError: non-default argument follows default argumentdef test(x, y, z=3): # x, y是形参
print("x is %d, y is %d, z is %d" % (x, y, z))
calc_age = x * y * z
return calc_age

# 只传两个参数,按照正常逻辑 x=1, y=3, z=3
print(test(1, 6))结果如下:x is 1, y is 6, z is 3
18def test(x, z, y=3): # x, y是形参
print("x is %d, y is %d, z is %d" % (x, y, z))
calc_age = x * y * z
return calc_age

# 只传两个参数,按照正常逻辑 x=1, y=3, z=3
print(test(3, 2))结果如下:x is 3, y is 3, z is 2
18如上实例可以知道,函数的形参设置默认参数的时候,默认参数应该放到非默认参数后面,否则按照位置参数传入则就会报错,除非传入实参都设置位置参数!
上面实例报错分析:




 
动态参数实例如下:
动态参数也称为非固定参数,若你的函数在定义时不确定用户想传入多少个参数,就可以使用动态参数,使用了动态参数你就可以动态扩展,想传入多少参数都行!
 
1、argsdef test(x, y, *args):
print("x is %d, y is %d, z is %s" % (x, y, args))

test(1, 2, 3, 4, 5, 6)结果如下:x is 1, y is 2, z is (3, 4, 5, 6) def test(x, y, *args):
print("x is %d, y is %d, z is %s" % (x, y, args))
print(type(args))

test(1, 2) # 不传入args的值
test(1, 2, (3, 4, 5)) # 传入一个元组
test(1, 2, [3, 4, 5]) # 传入一个列表
test(1, 2, {3, 4, 5}) # 传入一个集合
test(1, 2, {1: 'lucky', 2: 'jack'}) # 传入一个字典结果如下:x is 1, y is 2, z is ()
<class 'tuple'>
x is 1, y is 2, z is ((3, 4, 5),)
<class 'tuple'>
x is 1, y is 2, z is ([3, 4, 5],)
<class 'tuple'>
x is 1, y is 2, z is ({3, 4, 5},)
<class 'tuple'>
x is 1, y is 2, z is ({1: 'lucky', 2: 'jack'},)
<class 'tuple'>总结:由上面代码可以看出,*args会把多传入的实参变成一个元组的类型;传入其他数据类型也一样,成为元组中的一个元素;另函数中有*args与其他形参的时候,*args一定要写到其他形参的后面,否则传入的实参都会被传入到*args当中打印成元组;还有如果没有多出传入的实参即*args没有值的时候,*args为空,不会报错。
 
2、kwargsdef test(x, y, **kwargs):
print("x is %d, y is %d, z is %s" % (x, y, kwargs))
print(type(kwargs))

test(1, 2) # 不传入kwargs的值
test(1, 2, 3, 4, 5)结果如下:Traceback (most recent call last):
x is 1, y is 2, z is {}
File "/Users/crh/PycharmProjects/app/test.py", line 11, in <module>
<class 'dict'>
test(1, 2, 3, 4, 5)
TypeError: test() takes 2 positional arguments but 5 were givendef test(x, y, **kwargs):
print("x is %d, y is %d, z is %s" % (x, y, kwargs))
print(type(kwargs))

test(1, 2, name='lucky', age=18, job='IT')结果如下:x is 1, y is 2, z is {'job': 'IT', 'name': 'lucky', 'age': 18}
<class 'dict'>**kwargs会把多出的a=b这种类型的实参打印成字典的类型(要区分开与关键参数的区别,关键参数的实参有对应的形参)test_list = [8, 9, 10, 11]
test_dict = {'name': 'lucky', 'age': '18', 'job': 'it'}

def test(x, y, *args, **kwargs):
print("x is %d, y is %d, args is %s, kwargs is %s" % (x, y, args, kwargs))

test(1, 2, test_list, test_dict)结果如下:x is 1, y is 2, args is ([8, 9, 10, 11], {'job': 'it', 'name': 'lucky', 'age': '18'}), kwargs is {}如上所示为什么把把字典传入函数后,打印的**kwargs为空值呢?!  是这样的,传入的字典会被当成一个元素传入函数,所有被当成多余的实参传入到了*args里面,所以**kwargs的值才为空;那么有什么办法可以把字典传入到**kwargs呢?test_list = [8, 9, 10, 11]
test_dict = {'name': 'lucky', 'age': '18', 'job': 'it'}

def test(x, y, *args, **kwargs):
print("x is %d, y is %d, args is %s, kwargs is %s" % (x, y, args, kwargs))

test(1, 2, *test_list, **test_dict)结果如下:x is 1, y is 2, args is (8, 9, 10, 11), kwargs is {'name': 'lucky', 'job': 'it', 'age': '18'}这下结果就是我想要的了,所以如果要把列表或者字典放到函数中,指定关键符号就好。
 
3、局部变量def change_name(name):
print("before change:", name)
name = "采菊篱下"
print("after change", name)

change_name(name)

print("在外面看看name改了么?", name)结果如下:before change: Lucky chen
after change 采菊篱下
在外面看看name改了么? Lucky chen全局与局部变量:
在子程序中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量;全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序;当全局变量与局部变量同名时,在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用。





返回值

要想获取函数的执行结果,就可以用return语句把结果返回,可以返回任何你想得到的东西,多返回值返回的是一个元组。def test(x, y, z, name):
calc_age = x * y * z
print(name)
return calc_age, name

age = test(3, 2, 3, 'lucky')
print(age, type(age))结果如下:lucky
(18, 'lucky') <class 'tuple'>总结:
函数在执行过程中只要遇到return语句,就会停止执行并返回结果,所以 return 语句代表着函数的结束,跟循环中的break一样。如果未在函数中指定return,那这个函数的返回值为None 
 

递归函数

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。就是把函数体处理的结果,再次传到本函数再次执行。

递归特性:
必须有一个明确的结束条件每次进入更深一层递归时,问题规模相比上次递归都应有所减少递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出) 
def calc(n):
print(n)
if int(n/2) == 0:
return n
return calc(int(n/2))


calc(10)

Result:
10
5
2
1下面我们再来看一个斐波那契数列的例子:
def func(count, n1, n2): # 获取斐波那契数列第200个数字并返回给调用者
if count == 200:
return n1
n3 = n1 + n2
r = func(count + 1, n2, n3)
return r
ret = func(1, 0, 1)
print(ret)

Result:
173402521172797813159685037284371942044301

函数的作用域





再看一个例子:
name2 = "chenronghua"
def say():
name = "lucky"
print(name)
print(name2)
say()结果为:
lucky
chenronghua总结:
函数的作用域就是在函数里定义的变量不能被外面使用!但是外部全局定义的全局变量在函数内是可以使用的。
举个例子来说:你在房子里可以看到屋内的东西和房子外的东西,但是你在房子外面就只能看到房子外的东西不能看到房子内的东西!
原因防止在函数调用的时候防止变量冲突!
那问题来了,我在外面定义的全局变量在函数内可以改他吗,例子如下:
name2 = "chenronghua"
def say():
name = "lucky"
name2 = "crh"
print(name)
print("function inside name2 value %s" % name2)
say()
print("function outside name value %s" % name2)结果如下:
lucky
function inside name2 value crh
function outside name value chenronghua从上面的例子可以看出函数里面把name2的值改了,但是在外部打印还是没有发生改变,这就是函数作用域的诠释。
 
如果我就是想改,怎么办呢,可以吗?如下例子所示:
name2 = "chenronghua"
def say():
global name2
name = "lucky"
name2 = "crh"
print(name)
print("function inside name2 value %s" % name2)
say()
print("function outside name value %s" % name2)结果如下:
lucky
function inside name2 value crh
function outside name value crh可以看出,是可以做到的,你需要在函数体内利用内置函数global来声明你想作为全局变量的变量。 查看全部
在介绍Python的函数之前我们先来回顾一下,我们平常写代码是不是需要什么功能就写什么功能,但是到后面深入你会发现在写代码的过程中,有好多代码是重叠的,就是一个功能块在不同的地方都被使用,但是每次都得重新写一遍,这样就造成了写程序的效率问题,而Python中的函数就可以很好的解决这么一点。例如一个登陆代码快的代码,你比如刚登陆的时候需要验证用户名密码,你执行他事件比如购物、结算等逻辑的时候也需要做验证,那如果我们把这个验证的逻辑封装成一个函数,然后用到的地方,想怎么调就怎么调用,所以减少代码的重复性是函数的特性之一。
 
同时在过去的十年间,大家广为熟知的编程方法无非两种:面向对象和面向过程,其实,无论哪种,都是一种编程的规范或者是如何编程的方法论。而如今,一种更为古老的编程方式:函数式编程,以其不保存状态,不修改变量等特性重新进入人们的视野。下面我们就来依次了解这一传统的编程理念,下面我们介绍一些Python中函数的概念。
 
面向过程:根据业务逻辑从上到下写垒代码
函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可


函数是什么?


在我们的初中书写中我们应该知道3x  = 4y 这就是一个简单的函数,而x、y就是两个变量,如果x确定为一个值,那y的值就是确定的,我们把x叫做自变量,而把y叫做应变量,y是x的函数,当然也可以反过来说。这里就是y会随着x的值变化而变化。而这个自变量x的取值范围我们就叫做这个函数的定义域。
 
函数一词虽源于数学,但编程中的「函数」概念,与数学中的函数是有很大不同的,具体区别,我们后面会讲,编程中的函数在英文中也有很多不同的叫法。在BASIC中叫做subroutine(子过程或子程序),在Pascal中叫做procedure(过程)和function,在C中只有function,在Java里面叫做method。
 
定义: 函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名的方法即可

函数的特性:
  • 减少重复代码
  • 使程序变的可扩展(装饰器本质是函数)
  • 使程序变得易维护

 
语法定义:
def test(x):
"The function definitions"
x+=1
return x

******************************************************************
def:定义函数的关键字
test:函数名
():内可定义形参
"":文档描述(非必要,但是强烈建议为你的函数添加描述信息)
x+=1:泛指代码块或程序处理逻辑
return:定义返回值
**编程语言中函数定义:函数是逻辑结构化和过程化的一种编程方法,函数式编程就是先定义一个数学函数,然后按照这个数学模型用编程语言去实现它。
 
一个栗子
#!/usr/bin/env python3
# _*_coding:utf-8_*_
# Author: Lucky.chen

# 常规码代码
a, b = 5, 8
c = a * b
print(c)

print("我是分割线".center(30, '*'))
# 函数方法
def calc(x, y):
res = x * y
return res # 返回函数执行结果

c = calc(a, b) # 结果赋值给c变量
print(c)
Result:
40
************我是分割线*************
40
这不是功能一样吗,看起来并没有什么卵用!!!嘿嘿!


函数参数与局部变量


func.png

形参:
形式参数,不是实际存在,是虚拟变量。在定义函数和函数体的时候使用形参,目的是在函数调用时接收实参(实参个数,类型应与实参一一对应)。
实参:
实际参数,调用函数时传给函数的参数,可以是常量,变量,表达式,函数,传给形参。

区别:
形参是虚拟的,不占用内存空间,形参变量只有在被调用时才分配内存单元,实参是一个变量,占用内存空间,数据传送单向,实参传给形参,不能形参传给实参。
实参就是函数执行的时候用户调用函数实际传输的参数简称为实参,实参又分为位置参数和关键参数。
 
位置参数实例如下
def test(x, y):     # x, y是形参
print("x is %d, y is %d" % (x, y))
calc_age = x * y
return calc_age

# 3在前,6在后 那么传给函数的形参就是按照位置 x = 3 , y = 6
print(test(3, 6))
print("我是分割线".center(30, '*'))
# 如果调用的时候6在前,3在后,那么 x = 6, y = 3
print(test(6, 3))
结果如下:
x is 3, y is 6
18
************我是分割线*************
x is 6, y is 3
18
关键参数实例如下:
def test(x, y, z):     # x, y是形参
print("x is %d, y is %d, z is %d" % (x, y, z))
calc_age = x * y * z
return calc_age

# 指定y和z的值而x的值就是默认出入位置为第个的实参
print(test(3, y=2, z=3))
print("我是分割线".center(30, '*'))
# 指定y和z的值,但是y和z的位置对调
print(test(3, z=2, y=3))
结果如下:
x is 3, y is 2, z is 3
18
************我是分割线*************
x is 3, y is 3, z is 2
18
如果这样呢?
def test(x, y, z):     # x, y是形参
print("x is %d, y is %d, z is %d" % (x, y, z))
calc_age = x * y * z
return calc_age

# 我指定第一个实参的数值,而不指定y和z的呢?
print(test(x=3, 2, 3))
结果如下报错了:
  File "/Users/crh/PycharmProjects/app/test.py", line 12
print(test(x=3, 2, 3))
^
SyntaxError: positional argument follows keyword argument
如上实例介绍可以得出如下结论:
正常情况下,给函数传参数要按顺序,不想按顺序就可以用关键参数,只需指定参数名即可,但记住一个要求就是,关键参数必须放在位置参数之后
 
默认参数实例如下:
def test(x, y=3, z):     # x, y是形参
print("x is %d, y is %d, z is %d" % (x, y, z))
calc_age = x * y * z
return calc_age

# 只传两个参数,按照正常逻辑 x=1, y=3, z=3
print(test(1, 3))
# 但是报错了
报错如下:
  File "/Users/crh/PycharmProjects/app/test.py", line 6
def test(x, y=3, z): # x, y是形参
^
SyntaxError: non-default argument follows default argument
def test(x, y, z=3):     # x, y是形参
print("x is %d, y is %d, z is %d" % (x, y, z))
calc_age = x * y * z
return calc_age

# 只传两个参数,按照正常逻辑 x=1, y=3, z=3
print(test(1, 6))
结果如下:
x is 1, y is 6, z is 3
18
def test(x, z, y=3):     # x, y是形参
print("x is %d, y is %d, z is %d" % (x, y, z))
calc_age = x * y * z
return calc_age

# 只传两个参数,按照正常逻辑 x=1, y=3, z=3
print(test(3, 2))
结果如下:
x is 3, y is 3, z is 2
18
如上实例可以知道,函数的形参设置默认参数的时候,默认参数应该放到非默认参数后面,否则按照位置参数传入则就会报错,除非传入实参都设置位置参数!
上面实例报错分析:
args.png

 
动态参数实例如下:
动态参数也称为非固定参数,若你的函数在定义时不确定用户想传入多少个参数,就可以使用动态参数,使用了动态参数你就可以动态扩展,想传入多少参数都行!
 
1、args
def test(x, y, *args):
print("x is %d, y is %d, z is %s" % (x, y, args))

test(1, 2, 3, 4, 5, 6)
结果如下:
x is 1, y is 2, z is (3, 4, 5, 6)
 
def test(x, y, *args):
print("x is %d, y is %d, z is %s" % (x, y, args))
print(type(args))

test(1, 2) # 不传入args的值
test(1, 2, (3, 4, 5)) # 传入一个元组
test(1, 2, [3, 4, 5]) # 传入一个列表
test(1, 2, {3, 4, 5}) # 传入一个集合
test(1, 2, {1: 'lucky', 2: 'jack'}) # 传入一个字典
结果如下:
x is 1, y is 2, z is ()
<class 'tuple'>
x is 1, y is 2, z is ((3, 4, 5),)
<class 'tuple'>
x is 1, y is 2, z is ([3, 4, 5],)
<class 'tuple'>
x is 1, y is 2, z is ({3, 4, 5},)
<class 'tuple'>
x is 1, y is 2, z is ({1: 'lucky', 2: 'jack'},)
<class 'tuple'>
总结:由上面代码可以看出,*args会把多传入的实参变成一个元组的类型;传入其他数据类型也一样,成为元组中的一个元素;另函数中有*args与其他形参的时候,*args一定要写到其他形参的后面,否则传入的实参都会被传入到*args当中打印成元组;还有如果没有多出传入的实参即*args没有值的时候,*args为空,不会报错。
 
2、kwargs
def test(x, y, **kwargs):
print("x is %d, y is %d, z is %s" % (x, y, kwargs))
print(type(kwargs))

test(1, 2) # 不传入kwargs的值
test(1, 2, 3, 4, 5)
结果如下:
Traceback (most recent call last):
x is 1, y is 2, z is {}
File "/Users/crh/PycharmProjects/app/test.py", line 11, in <module>
<class 'dict'>
test(1, 2, 3, 4, 5)
TypeError: test() takes 2 positional arguments but 5 were given
def test(x, y, **kwargs):
print("x is %d, y is %d, z is %s" % (x, y, kwargs))
print(type(kwargs))

test(1, 2, name='lucky', age=18, job='IT')
结果如下:
x is 1, y is 2, z is {'job': 'IT', 'name': 'lucky', 'age': 18}
<class 'dict'>
**kwargs会把多出的a=b这种类型的实参打印成字典的类型(要区分开与关键参数的区别,关键参数的实参有对应的形参)
test_list = [8, 9, 10, 11]
test_dict = {'name': 'lucky', 'age': '18', 'job': 'it'}

def test(x, y, *args, **kwargs):
print("x is %d, y is %d, args is %s, kwargs is %s" % (x, y, args, kwargs))

test(1, 2, test_list, test_dict)
结果如下:
x is 1, y is 2, args is ([8, 9, 10, 11], {'job': 'it', 'name': 'lucky', 'age': '18'}), kwargs is {}
如上所示为什么把把字典传入函数后,打印的**kwargs为空值呢?!  是这样的,传入的字典会被当成一个元素传入函数,所有被当成多余的实参传入到了*args里面,所以**kwargs的值才为空;那么有什么办法可以把字典传入到**kwargs呢?
test_list = [8, 9, 10, 11]
test_dict = {'name': 'lucky', 'age': '18', 'job': 'it'}

def test(x, y, *args, **kwargs):
print("x is %d, y is %d, args is %s, kwargs is %s" % (x, y, args, kwargs))

test(1, 2, *test_list, **test_dict)
结果如下:
x is 1, y is 2, args is (8, 9, 10, 11), kwargs is {'name': 'lucky', 'job': 'it', 'age': '18'}
这下结果就是我想要的了,所以如果要把列表或者字典放到函数中,指定关键符号就好。
 
3、局部变量
def change_name(name):
print("before change:", name)
name = "采菊篱下"
print("after change", name)

change_name(name)

print("在外面看看name改了么?", name)
结果如下:
before change: Lucky chen
after change 采菊篱下
在外面看看name改了么? Lucky chen
全局与局部变量:
  • 在子程序中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量;
  • 全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序;
  • 当全局变量与局部变量同名时,在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用。

zuoyongyu.png


返回值


要想获取函数的执行结果,就可以用return语句把结果返回,可以返回任何你想得到的东西,多返回值返回的是一个元组。
def test(x, y, z, name):
calc_age = x * y * z
print(name)
return calc_age, name

age = test(3, 2, 3, 'lucky')
print(age, type(age))
结果如下:
lucky
(18, 'lucky') <class 'tuple'>
总结:
  • 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,所以 return 语句代表着函数的结束,跟循环中的break一样。
  • 如果未在函数中指定return,那这个函数的返回值为None 

 


递归函数


在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。就是把函数体处理的结果,再次传到本函数再次执行。

递归特性:
  1. 必须有一个明确的结束条件
  2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
  3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出) 

def calc(n):
print(n)
if int(n/2) == 0:
return n
return calc(int(n/2))


calc(10)

Result:
10
5
2
1
下面我们再来看一个斐波那契数列的例子:
def func(count, n1, n2):        # 获取斐波那契数列第200个数字并返回给调用者
if count == 200:
return n1
n3 = n1 + n2
r = func(count + 1, n2, n3)
return r
ret = func(1, 0, 1)
print(ret)

Result:
173402521172797813159685037284371942044301


函数的作用域


zyy.png

再看一个例子:
name2 = "chenronghua"
def say():
name = "lucky"
print(name)
print(name2)
say()
结果为:
lucky
chenronghua
总结:
函数的作用域就是在函数里定义的变量不能被外面使用!但是外部全局定义的全局变量在函数内是可以使用的。
举个例子来说:你在房子里可以看到屋内的东西和房子外的东西,但是你在房子外面就只能看到房子外的东西不能看到房子内的东西!
原因防止在函数调用的时候防止变量冲突!

那问题来了,我在外面定义的全局变量在函数内可以改他吗,例子如下:
name2 = "chenronghua"
def say():
name = "lucky"
name2 = "crh"
print(name)
print("function inside name2 value %s" % name2)
say()
print("function outside name value %s" % name2)
结果如下:
lucky
function inside name2 value crh
function outside name value chenronghua
从上面的例子可以看出函数里面把name2的值改了,但是在外部打印还是没有发生改变,这就是函数作用域的诠释。
 
如果我就是想改,怎么办呢,可以吗?如下例子所示:
name2 = "chenronghua"
def say():
global name2
name = "lucky"
name2 = "crh"
print(name)
print("function inside name2 value %s" % name2)
say()
print("function outside name value %s" % name2)
结果如下:
lucky
function inside name2 value crh
function outside name value crh
可以看出,是可以做到的,你需要在函数体内利用内置函数global来声明你想作为全局变量的变量。