嵌套函数
函数定义与使用nonlocal关键词 LEGB规则面向对象类
类的定义类和对象的关系构造函数实例属性实例方法
实例方法的使用其他操作 类对象类属性类方法静态方法__del__方法(析构函数)和垃圾回收机制__call__方法和可调用对象
嵌套函数 函数定义与使用def printname(isChnese,name,familyname):
def inner_print(a,b):
print('name:{}{}'.format(a,b))
if isChnese:
inner_print(familyname,name)
else:
inner_print(name,familyname)
printname(True,'三','张')
printname(False,'li','si')
#结果为:
name:张三
name:lisi
nonlocal关键词
nonlocal:用来声明外层的局部变量
global:用来声明全局变量
def outer():
b = 2
def inner():
print('打印b:',b)
inner()
outer()
#结果为:
打印b: 2
但如果要在嵌套函数中修改外层变量b则报错:
修改一:
def outer():
b = 2
def inner():
b = 20
print(b)
inner()
outer()
#结果为
20
此时外层变量b与嵌套函数内部的b变量名一样,嵌套函数会以局部变量为准,即内层嵌套的b,下例同理,将嵌套函数的b在使用后修改报错,因为变量相同时,嵌套函数内只认内部变量,因此print打印b时,b还未被定义(b的定义在打印语句下面):
def outer():
b = 2
def inner():
print('打印b:', b)
b = 20
inner()
outer()
#结果为(报错):
UnboundLocalError: local variable 'b' referenced before assignment
但在声明了b为nonlocal时:
def outer():
b = 2
def inner():
nonlocal b
print('打印b:', b)
b = 20
print('打印b:', b)
inner()
print('打印b:', b)
outer()
#结果为:
打印b: 2
打印b: 20
打印b: 20
LEGB规则
Python在查找名称时,是按LEGB规则查找的:local–>Enclosed–>Global–>Built in
str = 'global str'
def outer():
str = 'outer'
def inner():
str = 'inner'
print(str)
inner()
outer()
#结果为:
inner
将局部变量str注释后:
str = 'global str'
def outer():
str = 'outer'
def inner():
# str = 'inner'
print(str)
inner()
outer()
#结果为:
outer
在注释“outer”后:
str = 'global str'
def outer():
# str = 'outer'
def inner():
# str = 'inner'
print(str)
inner()
outer()
#结果为:
global str
面向对象
总结:
注:区分类与实例的属性和方法,二者不同!!!
类的定义类定义了数据类型的属性(数据)和方法(行为),也就是说,类将行为和状态打包在一起。
类名一般首字母大写。
类和对象的关系如果把对象比作一个饼干,则类就是制造饼干的模具。
对象也可以包含方法,对象是类的实例。
在Python中一切皆对象,类也称为类对象,类的实例也称为实例对象。
我们可以进一步说,一个Python对象包含如下部分:
1、id
2、type
3、value
(1)属性
(2)方法
样例(很重要!理解!):
class Student:
def __init__(self,name,score):
self.name_1 = name
self.score_1 = score
def print_information(self):
print('{}的分数为:{}'.format(self.name_1,self.score_1))
print(Student)
print(type(Student))
S = Student('zzz',96)
print(S)
print(type(S))
S.print_information()
#结果为:
<__main__.Student object at 0x0000022B28AD9710>
zzz的分数为:96
解析:
Student为一个类,它的type是‘type’型,等同于一个模具,但模具内没有实例属性 (没有创建实例时实例属性并不存在,创建了实例后实例属性也是在实例对象中,而非模具中,所以下面提到的类方法和静态方法均不能访问实例属性, 看下图);S是创建的一个实例对象,它的type是一个类。
S = Student(‘zzz’,96)是将创建的实例对象的id赋值给了S。
在实例对象中,实则通过Student模具创建了实例对象,有id、type、和value,其中value又包含有属性和方法,所以创建的实例对象中有两个属性:name_1和score_1,而S = Student(‘zzz’,96)就是将对象‘zzz’和96的id赋值给了name_1和score_1,其实跟函数的赋值差不多。
方法的调用实际上是每个实例内共享了模具里方法的代码,但每个实例对象的方法id都是不一样的。
再来一个示例:
图解:
init()方法
其本质有点像函数的赋值,将参数赋给了构造函数的形参,然后再赋给实例对象的参数属性。
注:图片中的say_score()等同于样例中的print_information()
直接使用类Student.say_score(a)时,解释器不会自动将self参数传入,所以需要在括号内加a,其作用就是将self地址传入,由下例可知,二者作用一样:
class Student:
def __init__(self,name,score):
self.name_1 = name
self.score_1 = score
def print_information(self):
print('{}的分数为:{}'.format(self.name_1,self.score_1))
S = Student('zzz',97)
S.print_information()
Student.print_information(S)
#结果为:
zzz的分数为:97
zzz的分数为:97
其他操作
class Student:
def __init__(self):
self.name_1 = 'zzz'
self.score_1 = 97
def print_information(self):
print('{}的分数为:{}'.format(self.name_1,self.score_1))
class Test:
pass
S = Student()
print(isinstance(S,Student))
print(isinstance(S,Test))
#结果为:
True
False
类对象
class Student:
pass
print(id(Student))
s = Student()
k = Student
z = k()
print(id(s))
print(id(k))
print(id(z))
#结果为:
2372211609384
2372218500880
2372211609384
2372220195840
print(id(Student))和print(id(k))的结果一样,他们都是类对象,而s和z则是根据类模板创造出的两个实例对象,因此id不一样
以下案例可以验证,Student的type是‘模具类’,即‘type’,而Student()是一个根据Student模具创建的实例对象
class Student:
pass
print(type(Student))
S = Student()
print(S)
#结果为:
<__main__.Student object at 0x000001BB0CCEB128>
类属性
图解:
class Test:
age = 18
@classmethod
def inner(cls):
print('打印:',cls.age)
Test.inner()
#结果为:
打印:18
静态方法
但静态方法可以访问类属性。
class Test:
age = 18
@classmethod
def inner(cls):
print('打印:',cls.age)
@staticmethod
def add(a,b):
print(a+b)
Test.add(1,6)
#结果为:
7
__del__方法(析构函数)和垃圾回收机制
下面重写了析构方法:
class Preson:
def __del__(self):
print('销毁:{}'.format(self))
s1 = Preson()
s2 = Preson()
print('over')
#结果为:
over
销毁:<__main__.Preson object at 0x0000021B4E1FB198>
销毁:<__main__.Preson object at 0x0000021B4E3993C8>
即创建两个实例对象后打印over,然后对象不使用则回收,调用析构方法来销毁。
下面用了del函数:
class Preson:
def __del__(self):
print('销毁:{}'.format(self))
s1 = Preson()
s2 = Preson()
del s2
print('over')
#结果为:
销毁:<__main__.Preson object at 0x00000130565993C8>
over
销毁:<__main__.Preson object at 0x00000130563FB198>
即创建了两个实例后调用del函数,销毁了s2,因此结果的第一行为s2的销毁结果,然后打印over,接着s1没有调用自动销毁。
__call__方法和可调用对象定义了__call__方法的对象,称为“可调用对象”,即该对象可以像函数一样被调用。
其实就是()的用法!看下例:
class Test:
def __call__(self, a):
print(a)
return a*2
s = Test()
j = s.__call__(5)
k = s(5)
print(j,k)
#结果为:
5
5
10 10
s()中的()就是调用了__call__方法。



