Python随笔—基础篇

前言

这篇博文是南国关于python的第一篇博客,之前对于python的学习一直断断续续。因为个人倾向于用Java解决问题,但不可否认Python某些方面有他的优势和特点。这篇博客 主要回顾下Python之于Java C++不同的基础知识。本篇博客编写时许多部分参考了菜鸟教程的讲解。话不多说,干货来了~

基础语法

标准数据类型

Python3 中有六个标准的数据类型:

  • Number(数字)
  • String(字符串)
  • List(列表)
  • Tuple(元组)
  • Set(集合)
  • Dictionary(字典)

Python3 的六个标准数据类型中:

  • 不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);
  • 可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。

数字(Number)类型

python中数字有四种类型:整数、布尔型、浮点数和复数

  • int (整数), 如 1, 只有一种整数类型 int,表示为长整型,没有 python2 中的 Long。
  • bool (布尔), 如 True。
  • float (浮点数), 如 1.23、3E-2
  • complex (复数), 如 1 + 2j、 1.1 + 2.2j

内置的 type() 函数可以用来查询变量所指的对象类型。

>>> a, b, c, d = 20, 5.5, True, 4+3j
>>> print(type(a), type(b), type(c), type(d))
<class 'int'> <class 'float'> <class 'bool'> <class 'complex'>

此外还可以用 isinstance 来判断:

>>>a = 111
>>> isinstance(a, int)
True

isinstance 和 type 的区别在于:

  • type()不会认为子类是一种父类类型。
  • isinstance()会认为子类是一种父类类型。
>>> class A:
...     pass
... 
>>> class B(A):
...     pass
... 
>>> isinstance(A(), A)
True
>>> type(A()) == A 
True
>>> isinstance(B(), A)
True
>>> type(B()) == A
False

关于数值计算:

>>>5 + 4  # 加法
9
>>> 4.3 - 2 # 减法
2.3
>>> 3 * 7  # 乘法
21
>>> 2 / 4  # 除法,得到一个浮点数
0.5
>>> 2 // 4 # 除法,得到一个整数
0
>>> 17 % 3 # 取余 
2
>>> 2 ** 5 # 乘方
32

注意:
1、Python可以同时为多个变量赋值,如a, b = 1, 2。
2、一个变量可以通过赋值指向不同类型的对象。
3、数值的除法包含两个运算符:/ 返回一个浮点数,// 返回一个整数。
4、在混合计算时,Python会把整型转换成为浮点数。

字符串(String)

  1. python中单引号和双引号使用完全相同。
  2. 使用三引号(’’'或""")可以指定一个多行字符串。
  3. 转义符。反斜杠\可以用来转义,使用r可以让反斜杠不发生转义。。 如 r"this is a line with \n" 则\n会显示,并不是换行。
  4. 按字面意义级联字符串,如"this " "is " "string"会被自动转换为this is string。
  5. 字符串可以用 + 运算符连接在一起,用 * 运算符重复。
  6. Python 中的字符串有两种索引方式,从左往右以 0 开始,从右往左以 -1 开始。
  7. Python中的字符串不能改变。
  8. Python 没有单独的字符类型,一个字符就是长度为 1 的字符串。
  9. 字符串的截取的语法格式如下:变量[头下标:尾下标:步长]

索引值以 0 为开始值,-1 为从末尾的开始位置。
在这里插入图片描述
加号 + 是字符串的连接符, 星号 * 表示复制当前字符串,与之结合的数字为复制的次数。实例如下:

'''
@author xjh 2020.3.9
'''
str='python'
print (str)          # 输出字符串
print (str[0:-1])    # 输出第一个到倒数第二个的所有字符
print (str[0])       # 输出字符串第一个字符
print (str[2:5])     # 输出从第三个开始到第五个的字符
print (str[2:])      # 输出从第三个开始的后的所有字符
print (str * 2)      # 输出字符串两次,也可以写成 print (2 * str)
print (str + " and pycharm") # 连接字符串

输出结果:

python
pytho
p
tho
thon
pythonpython
python and pycharm

List 列表

List(列表) 是 Python 中使用最频繁的数据类型。

列表可以完成大多数集合类的数据结构实现。列表中元素的类型可以不相同,它支持数字,字符串甚至可以包含列表(所谓嵌套)。

列表是写在方括号 [] 之间、用逗号分隔开的元素列表。

和字符串一样,列表同样可以被索引和截取,列表被截取后返回一个包含所需元素的新列表。

列表截取的语法格式如下:
变量[头下标:尾下标]

索引值以 0 为开始值,-1 为从末尾的开始位置。

与Python字符串不一样的是,列表中的元素是可以改变的:

>>>a = [1, 2, 3, 4, 5, 6]
>>> a[0] = 9
>>> a[2:5] = [13, 14, 15]
>>> a
[9, 2, 13, 14, 15, 6]
>>> a[2:5] = []   # 将对应的元素值设置为 [] 
>>> a
[9, 2, 6]

注意:
1、List写在方括号之间,元素用逗号隔开。
2、和字符串一样,list可以被索引和切片。
3、List可以使用+操作符进行拼接。
4、List中的元素是可以改变的。

下面是list的一个实例:

def reverse(input):
    list=input.split(" ")
    # 翻转字符串
    # 假设列表 list = [1,2,3,4],
    # list[0]=1, list[1]=2 ,而 -1 表示最后一个元素 list[-1]=4 ( 与 list[3]=4 一样)
    # inputWords[-1::-1] 有三个参数
    # 第一个参数 -1 表示最后一个元素
    # 第二个参数为空,表示移动到列表末尾
    # 第三个参数为步长,-1 表示逆向
    list=list[-1::-1]
    output=' '.join(list)
    return output

if __name__=='__main__':
    classmates = ['kobe', 'James', 'KD']  # 定义一个list,元素集合用[]
    print('classmates=', classmates)
    print(len(classmates))  # 获取list元素的个数

    classmates.append('Harden')
        #append 在末尾添加元素; 删除末尾元素用pop(),要删除指定位置的元素,用pop(i)方法,其中i是索引位置
    print(classmates)
    classmates.insert(2, 'Jordan')  # 在list指定下标位置插入特定元素
    print(classmates)

    s = [123, '123', ['apple', 'Huawei'], 'school']
    print('len: %s' % len(s) + '; s[2]= %s' % s[2])

    input="python java jetbrain"
    print(reverse(input))

输出结果:

classmates= ['kobe', 'James', 'KD']
3
['kobe', 'James', 'KD', 'Harden']
['kobe', 'James', 'Jordan', 'KD', 'Harden']
len: 4; s[2]= ['apple', 'Huawei']
jetbrain java python

列表的应用有以下几个方面

  • 将列表作为堆栈使用;
  • 将列表作为队列使用
  • 列表生成式

1.列表作为堆栈(先进后出)。用 append() 方法可以把一个元素添加到堆栈顶。用不指定索引的 pop() 方法可以把一个元素从堆栈顶释放出来。

stack=[1,2,3,4,5]
print(stack)
stack.pop()
stack.pop()
print('the result after deleting elements: ',stack)
stack.append(6)
stack.append(7)
t=[10,11,12]
stack.extend(t)
print('the result after adding elements: ',stack)

结果:

[1, 2, 3, 4, 5]
the result after deleting elements:  [1, 2, 3]
the result after adding elements:  [1, 2, 3, 6, 7, 10, 11, 12]

2.列表作为队列使用,但是这样的效率不高。在列表的最后添加或者弹出元素速度快,然而在列表里插入或者从头部弹出速度却不快(因为所有其他的元素都得一个一个地移动)。

# 队列使用
queue=deque(['Alex','Bob','Cris','David'])
print(queue)
queue.append('Frank')
queue.append('Gianna')
print('the result after adding elements: ',queue)
queue.popleft()
queue.popleft()
print('the result after deleting elements: ',queue)

结果:

deque(['Alex', 'Bob', 'Cris', 'David'])
the result after adding elements:  deque(['Alex', 'Bob', 'Cris', 'David', 'Frank', 'Gianna'])
the result after deleting elements:  deque(['Cris', 'David', 'Frank', 'Gianna'])

3.列表生成式。最大的优势在于简化了代码编写量。每个列表推导式都在 for 之后跟一个表达式,然后有零到多个 for 或 if 子句。返回结果是一个根据表达从其后的 for 和 if 上下文环境中生成出来的列表。如果希望表达式推导出一个元组,就必须使用括号。

L1 = ['Hello', 'World', 18, 'Apple', None]
L2 = [x.lower() for x in L1 if isinstance(x, str)] # 列表生成式,将list中所有字符串小写输出
print('L2= ',L2)

Tuple 元组

元组tuple,和list非常类似,但是tuple一旦初始化就不能修改,它也没有append(),insert()这样的方法。可以通过下标来访问。

 t1=('KB','LBJ','KD')    #定义一个tuple,元素集合用()
    print('t1= ',t1)
    print(t1[2])

    #“可变”的tuple
    t2=(123,'abc',['A','B'])    #tuple中的元素还可以是list
    print(t2[2][0])
    t2[2][0]='XX'   # 改变的是list中的元素,tuple中的每个指向不变
    print('t2= ',t2)

输出结果:

t1=  ('KB', 'LBJ', 'KD')
KD
A
t2=  (123, 'abc', ['XX', 'B'])

构造包含 0 个或 1 个元素的元组比较特殊,所以有一些额外的语法规则:

  • tup1 = () # 空元组
  • tup2 = (20,) # 一个元素,需要在元素后添加逗号

Set 集合

集合(set)是由一个或数个形态各异的大小整体组成的,构成集合的事物或对象称作元素或是成员。

基本功能是进行成员关系测试和删除重复元素。

可以使用大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。

创建格式:
parame = {value01,value02,…}
或者
set(value)

Dictionary字典

字典(dictionary)是Python中另一个非常有用的内置数据类型。

列表是有序的对象集合,字典是无序的对象集合。两者之间的区别在于:字典当中的元素是通过键来存取的,而不是通过偏移存取。

字典是一种映射类型,字典用 { } 标识,它是一个无序的 键(key) : 值(value) 的集合。

键(key)必须使用不可变类型。

在同一个字典中,键(key)必须是唯一的。

注意:
1、字典是一种映射类型,它的元素是键值对。
2、字典的关键字必须为不可变类型,且不能重复。
3、创建空字典使用 { }。

下面是关于dict和set的一个实例:

'''
dict和set的简单使用
dict 也被称为map,存放key/value元素
set 存放没有重复的key
set和dict唯一的区别是是否有key对应的value值。
但是set和dict一样无法存放可变的对象,因为无法判断两个可变对象是否相等
@author xjh 2019.12.04
'''
if __name__=='__main__':
    d={'aa':90,'bb':80,'cc':70,'dd':60,'ee':50} # 定义一个dict 元素集合用{}表示
    print(d['aa']) # 输出特定key对应的value
    print('cc' in d)   # 判断某个key是否在dict中存在,in输出的结果是True/False
    d['aa']=100     # 修改某个key对应的value
    print(d['aa'])  # 输出特定key对应的value
    print(d)
    d.pop('ee') # pop(key)进行元素删除
    print(d)

    s=set([1,2,3,3,4,5,5,7])    # 定义一个set,python中用set()表示,这里使用一个list作为set的参数
    print(s)    # 输出set,会自动过滤其中重复的元素
    s.add(10)   # 用add()在set中添加元素,remove()删除元素
    print(s)

输出结果:

90
True
100
{'cc': 70, 'ee': 50, 'aa': 100, 'bb': 80, 'dd': 60}
{'cc': 70, 'aa': 100, 'bb': 80, 'dd': 60}
{1, 2, 3, 4, 5, 7}
{1, 2, 3, 4, 5, 7, 10}

关于dict中的元素遍历:

d={1:'a',2:'b',3:'c'}
for key in d: #迭代key
    print(key)

for value in d.values(): #迭代value
    print(value)

for k,v in d.items(): #同时迭代key value   相当于Java中map的Entry
    print(k,v)

注意:
s.update( “字符串” ) 与 s.update( {“字符串”} ) 含义不同:

  • s.update( {“字符串”} ) 将字符串添加到集合中,有重复的会忽略。
  • s.update( “字符串” ) 将字符串拆分单个字符后,然后再一个个添加到集合中,有重复的会忽略。
>>> thisset = set(("Google", "Runoob", "Taobao"))
>>> print(thisset)
{'Google', 'Runoob', 'Taobao'}
>>> thisset.update({"Facebook"})
>>> print(thisset) 
{'Google', 'Runoob', 'Taobao', 'Facebook'}
>>> thisset.update("Yahoo")
>>> print(thisset)
{'h', 'o', 'Facebook', 'Google', 'Y', 'Runoob', 'Taobao', 'a'}

迭代器和生成器

迭代器 想必大家都很了解了,这里做个简单回顾:

  • 迭代器是一个可以记住遍历的位置的对象。
  • 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
  • 迭代器有两个基本的方法:iter() 和 next()。

这里需要指出来的时把一个类作为一个迭代器使用需要在类中实现两个方法 _iter_() 与 next()
_iter_() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 _next_() 方法并通过 StopIteration 异常标识迭代的完成。
_next_() 方法(Python 2 里是 next())会返回下一个迭代器对象。

生成器,Python中 使用了yield的函数称为生成器(generator)
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。

在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

调用一个生成器函数,返回的是一个迭代器对象。

输入输出

输出 print

'''
@author xjh 2020.3.9
print 默认输出是换行的,如果要实现不换行需要在变量末尾加上 end=""
'''

x='a'; y='b'    #Python可以在同一行中使用多条语句,语句之间使用分号(;)分割
print(x); print(y)  #换行输出
print(x,end=" ");print(y,end="")    #不换行输出

结果:

a
b
a b

关键字end可以用于将结果输出到同一行,或者在输出的末尾添加不同的字符。

输入 input

Python提供了 input() 内置函数从标准输入读入一行文本,默认的标准输入是键盘。

input 可以接收一个Python表达式作为输入,并将运算结果返回。

#coding=utf-8
def hello():
    return "hello "

name = input("please input your name: "); # 使用input()输入
print(name)
print(hello()+ "changsha")
print(hello(),"China")

结果:

please input your name: xjh
xjh
hello changsha
hello  China

读写文件 open read write

1.打开文件。open() 将会返回一个 file 对象,基本语法格式如下:

  • open(filename, mode)

filename:包含了你要访问的文件名称的字符串值。
mode:决定了打开文件的模式:只读,写入,追加等。所有可取值见如下的完全列表。这个参数是非强制的,默认文件访问模式为只读®。

2.读取文件内容。
为了读取一个文件的内容,调用 f.read(size), 这将读取一定数目的数据, 然后作为字符串或字节对象返回。
size 是一个可选的数字类型的参数。 当 size 被忽略了或者为负, 那么该文件的所有内容都将被读取并且返回。
f.readline() 会从文件中读取单独的一行。换行符为 ‘\n’。f.readline() 如果返回一个空字符串, 说明已经已经读取到最后一行。

3.写入文件。f.write(string) 将 string 写入到文件中, 然后返回写入的字符数。

# 打开一个文件
f = open("test,txt", "w")

num = f.write( "Python 是一个非常好的语言。\n是的,的确非常好!!\n" )
print(num)
# 关闭打开的文件
f.close()

4.关闭文件。当你处理完一个文件后, 调用 f.close() 来关闭文件并释放系统的资源,如果尝试再调用该文件,则会抛出异常。

错误和异常

Python中异常处理用的是try…except…finally 乍一看是不是和Java中的try…catch…finally惊人的相似,没错。你甚至可以理解为二者就是讲的同一件事。
Python 使用 raise 语句抛出一个指定的异常。你也可以理解为就是我们平时说的throw。 这里暂时就不做过多介绍。

面向对象

Python中的类提供了面向对象编程的所有基本功能:类的继承机制允许多个基类(c++衍生的特点:可多继承),派生类可以覆盖基类中的任何方法,方法中可以调用基类中的同名方法
对象可以包含任意数量和类型的数据。

类对象

举个例子:

class MyClass:
    i=123
    def test(self):
        return 'oop'

x=MyClass()
print(x.i)
print(x.test())

结果:

123
oop

实际上,类有一个名为 init() 的特殊方法(构造方法),该方法在类实例化时会自动调用。当然,你也可以自己编写构造方法。
self代表类的实例,而非类。类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。当然,self不是关键字,你也可以把它进行替换,但一般很少这样做。

class Test:
    def prt(self):
        print(self)
        print(self.__class__)
 
t = Test()
t.prt()

结果:

<__main__.Test instance at 0x100771878>
__main__.Test

从执行结果可以很明显的看出,self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类。
替换self为别的,例如temp:

class Test:
    def prt(temp):
        print(temp)
        print(temp.__class__)
 
t = Test()
t.prt()

结果:

<__main__.Test instance at 0x100771878>
__main__.Test

类的方法

如之前的例子所言,使用def来定义一个方法,与一般函数定义不同,类方法必须包含参数 self, 且为第一个参数,self 代表的是类的实例。

class people:
    #定义基本属性
    name = ''
    age = 0
    #定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0
    #定义构造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 说: 我 %d 岁,。" %(self.name,self.age))
 
# 实例化类
p = people('James',35,300)
p.speak()

结果:
James 说: 我 35 岁,200 磅。

继承

Python支持单继承 和多继承:

class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>

需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。

例如:
1.单继承demo

class student(people):
    grade = ''
    def __init__(self,n,a,w,g):
        #调用父类的构造方法
        people.__init__(self,n,a,w)
        self.grade = g
    #覆写父类的方法
    def speak(self):
        print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade))
s=student('David',10,40,3)
s.speak()

结果:
David 说: 我 10 岁了,我在读 3 年级

2.多继承demo

'''
面向对象
@author xjh 2020.3.13
'''
class people:
    #定义基本属性
    name = ''
    age = 0
    #定义私有属性,私有属性在类外部无法直接进行访问
    __weight = 0
    #定义构造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 说: 我 %d 岁,%d 磅。" %(self.name,self.age,self.__weight))
#单继承示例
class student(people):
    grade = ''
    def __init__(self,n,a,w,g):
        #调用父类的构造方法
        people.__init__(self,n,a,w)
        self.grade = g
    #覆写父类的方法
    def speak(self):
        print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade))


# 另一个类,多重继承之前的准备
class speaker():
    topic = ''
    name = ''
    def __init__(self, n, t):
        self.name = n
        self.topic = t
    def speak(self):
        print("我叫 %s,我是一个演说家,我演讲的主题是 %s" % (self.name, self.topic))

# 多重继承
class sample(speaker, student):
    a = ''
    def __init__(self, n, a, w, g, t):
        student.__init__(self, n, a, w, g)
        speaker.__init__(self, n, t)

p=people('James',35,200)
p.speak()
s=student('David',10,40,3)
s.speak()

test = sample("Tim",25,80,4,"Python")
test.speak()   #方法名同,默认调用的是在括号中排前地父类的方法

结果:

James 说: 我 35 岁,200 磅。
David 说: 我 10 岁了,我在读 3 年级
我叫 Tim,我是一个演说家,我演讲的主题是 Python

类属性和方法

Python中,如果一个类属性或者方法以两个下划线开头,则表示私有,不能在类的外部被使用或直接访问。无,则是公开,可在类的外部直接访问。
例如:

class JustCounter:
    __secretCount = 0  # 私有变量
    publicCount = 0    # 公开变量
 
    def count(self):
        self.__secretCount += 1
        self.publicCount += 1
        print (self.__secretCount)
 
counter = JustCounter()
counter.count()
counter.count()
print (counter.publicCount)
print (counter.__secretCount)  # 报错,实例不能访问私有变量

作用域

Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。

变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python的作用域一共有4种,分别是:

有四种作用域:

  • L(Local):最内层,包含局部变量,比如一个函数/方法内部。
  • E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。
  • G(Global):当前脚本的最外层,比如当前模块的全局变量。
  • B(Built-in): 包含了内建的变量/关键字等。,最后被搜索

规则顺序: L –> E –> G –> B。

在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内置中找。
举个例子:Python 的一个内建值 int,我们首先将其赋值为 0,然后定义一个函数 fun1()。

int = 0
def fun1():
    int = 1
    def fun2():
        int = 2
        print(int)
	fun2()
fun1() #输出为2

函数 fun1() 的作用就是调用函数 fun2() 来打印 int 的值。
调用函数fun1() 输出结果为2。这是因为local中的int=2, 函数将其打印出来。

当我们将fun2()中的int=2删除:

int = 0
def fun1():
    int = 1
    def fun2():
        print(int)
	fun2()
fun1() #输出为1

调用函数fun1() 输出结果为1。因为 local 找不到 int 的值,就去上一层 non-local 寻找,发现 int = 1 并打印。

进一步删除函数 fun1() 中的 int = 1:

int = 0
def fun1():
    def fun2():
        print(int)
	fun2()
fun1() #输出为0

因为 local 和 non-local 都找不到 int 的值,便去 global 中寻找,发现 int = 0 并打印。

若删除 int = 0这一条件:

def fun1():
    def fun2():
        print(int)
	fun2()
fun1() #输出为<class 'int'>

全局变量和局部变量

定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。如下实例:

total = 0 # 这是一个全局变量
# 可写函数说明
def sum( arg1, arg2 ):
    #返回2个参数的和."
    total = arg1 + arg2 # total在这里是局部变量.
    print ("函数内是局部变量 : ", total)
    return total
 
#调用sum函数
sum( 10, 20 )
print ("函数外是全局变量 : ", total)

结果:

函数内是局部变量 :  30
函数外是全局变量 :  0

global 和 nonlocal关键字

当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了。

num = 1
def fun1():
    global num  # 需要使用 global 关键字声明
    print(num) 
    num = 123
    print(num)
fun1()
print(num)

结果:

1
123
123

如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal 关键字了,如下实例:

def outer():
    num = 10
    def inner():
        nonlocal num   # nonlocal关键字声明
        num = 100
        print(num)
    inner()
    print(num)
outer()

结果:

100
100

讲到nolocal关键字, 引申出下一个比较重要的知识点,闭包。

闭包

Python中使用闭包主要是在进行函数式开发使用。他有一个很通俗的理解:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure).
举例:

>>>def addx(x):
>>>    def adder(y): return x + y
>>>    return adder
>>> c =  addx(8)
>>> type(c)
<type 'function'>
>>> c.__name__
'adder'
>>> c(10)
18

结合这段简单的代码和定义来说明闭包:
如果在一个内部函数里:adder(y)就是这个内部函数,
对在外部作用域(但不是在全局作用域)的变量进行引用:x就是被引用的变量,x在外部作用域addx里面,但不在全局作用域里,
则这个内部函数adder就是一个闭包。

再稍微讲究一点的解释是,闭包=函数块+定义函数时的环境,adder就是函数块,x就是环境,当然这个环境可以有很多,不止一个简单的x。

关于闭包的知识点,在这里推荐一篇不错的文章:Python中的闭包

最后写一个关于闭包的应用demo:

'''
@author xjh 2020.3.7
闭包:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)
'''

# 利用闭包返回一个计数器函数,每次调用它返回递增整数
def createCounter():
    count=0
    def counter():
        nonlocal count  #声明为非局部变量
        count+=1
        return count
    return counter

counterA = createCounter()
print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5
counterB = createCounter()
if [counterB(), counterB(), counterB(), counterB()] == [1, 2, 3, 4]:
    print('测试通过!')
else:
    print('测试失败!')

结果:

1 2 3 4 5
测试通过!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章