Day7:嵌套函数、LEGB规则,面向对象编程及类的初识
文章目录
嵌套函数
在函数内部定义的函数
def outer():
print("outer")
def inner():
print("inner")
inner()
outer()
一般在什么情况下使用嵌套函数?
-
封装——数据隐藏
外部无法访问“嵌套函数”;
-
贯彻DRY(Don’t Repeat Yourself)原则
嵌套函数,可以让我在函数内部避免重复代码;
-
闭包
后面会详解;
# 练习
def print_name(is_chinese, name, family_name):
def inner_print(a, b):
print("{0} {1}".format(a, b))
if is_chinese:
inner_print(family_name, name)
else:
inner_print(name, family_name)
print_name(True, "裤子", "彭")
print_name(False, "Alex", "Rose")
nonlocal关键字
- nonlocal:用来声明外层的变量
- global:用来声明全局变量
a = 100
def outer():
b = 10
def inner():
nonlocal b # 如果要在内部函数中对外部函数的变量进行就行,则需要通过nonlocal来声明,内部函数的b并不是局部变量
print("inner:", b)
b = 30
inner()
print("outer b:", b)
global a # 声明全局变量
a = 200
print(a)
outer()
print(a) # 如不对global a进行声明,则此处a = 100
LEGB原则
# 此处注释掉,依然不报错,因为str()是python的内置函数
str = "global str" # Global
def outer():
str = "outer" # Enclosed
def inner():
str = "inner" # Local
print(str)
inner()
outer()
面向对象编程
面向对象和面向过程的区别
- 面向过程(Procedure Oriented)思维
-
面向对象(Object Oriented)
面向对(Object)思想更契合人的思维模式。我们首先思考的是“怎么设计这个事物?”比如思考造车,我们会先思考"车怎么设计",而不是“怎么按照步骤在造车的问题”。这就是思维方式的转变。
-
面向对象和面向过程的总结
- 都是解决问题的思维方式,都是代码组织的方式
- 解决简单问题可以使用面向过程
- 解决复杂问题:宏观上使用面向对象把握,微观处理上仍然是面向过程
对象的进化
类的定义
我们可以把类理解为制作事物的模具,而制作出来的事物就是对象。
- 对象是类的具体实体,一般称为“类的实例”,从一个类创建对象时,每个对象都会共享这个类的行为(类中定义的方法),但会有自己的属性值(不共享状态)。更具体一点:“方法的代码是共享的,属性数据不共享”。
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def say_score(self):
print("{0}的分数是:{1}".format(self.name, self.score))
s1 = Student("彭裤子", 100)
s1.say_score()
构造函数 __ init __()
4.通过“类名(参数列表)”来调用构造函数。调用后,将创建好的对象返回给相应的变量。
实例属性
- 当执行s1.age、s1.salary时,会在s1的内存空间增加这两个属性;
- 当删除对象后del obj,从属与对象的属性,也会随之删除;
实例方法
- 定义实例方法时,第一个参数必须为self,和前面一样,self指当前的实例对象。
- 调用实例方法时,不需要也不能给self传参,self由解释器自动传参。
-
先通过a = Student()进行实例化,即在内存中开辟一块属于a的内存空间,a.say_score(60)相当于自动把a的内存地址(self)和需要传递的参数穿到类内存空间中对应的方法,进行调用。
-
而Student.say_score(a),相当于直接在类的内存空间内调用say_score()方法,只不过穿的参数是对象a的内存地址,因此和上面的a.say_score(60)等效。
其他操作
- dir(obj)可以获得对象的所有属性、方法
- obj.__ dict __对象的属性字典
- pass 空语句
- isinstance(对象,类型) 判断“对象” 是不是“指定类型”
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def say_score(self):
print("{0}的分数是:{1}".format(self.name, self.score))
s1 = Student("彭裤子", 100)
s1.say_score()
print(dir(s1))
'''
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'say_score', 'score']
'''
print(s1.__dict__) # {'name': '彭裤子', 'score': 100}
print(isinstance(s1, Student)) # True
类对象
我们在前面讲的定义格式中,“class 类名:”。实际上,当解释器执行class语句时,就会创建一个类对象。
print(type(Student)) # <class 'type'>
print(type(s1)) # <class '__main__.Student'>
- 各种的类,如Student都属于type类
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def say_score(self):
print("{0}的分数是:{1}".format(self.name, self.score))
stu2 = Student # 把类对象Student的地址给了stu2
s1 = Student("超载", 100)
s2 = stu2("地下婴儿", 90)
s1.say_score() # 超载的分数是:100
s2.say_score() # 地下婴儿的分数是:90
类属性和方法
类属性
类属性是从属于“类对象”的属性,也称为“类变量”。由于,类属性从属于类对象,可以被所有的实例对象共享。
-
类属性的定义方式:
- class 类名:
- 类变量名 = 初始值
在类中或者类的外面,我们可以通过:“类名.类变量名”来读写。
- class 类名:
class Student:
company = "STX" # 类属性
count = 0 # 类属性
def __init__(self, name, score):
self.name = name # 实例属性
self.score = score
Student.count += 1 # 相当于计数器
def say_score(self): # 实例方法
print("{0}的分数是:{1}".format(self.name, self.score))
s1 = Student("便利商店", "70") # s1是实例对象,自动调用__init__()方法
s1.say_score()
s2 = Student("咖喱3000", 100)
s3 = Student("大波浪", 120)
print("一共创建{0}个student对象".format(Student.count))
类方法
- 类的方法只从属于类本身。
class Student:
company = "SXT"
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def print_company(cls):
print(cls.company)
# print(self.name) 类方法与静态方法中,不能调用实例变量、实例方法
Student.print_company()
静态方法
# 静态方法
class Student:
company = "SXT"
@staticmethod
def print_company(a, b):
print("{0}+{1}={2}".format(a, b, (a + b)))
Student.print_company(20, 30)
__ del __()析构方法
class Person:
def __del__(self):
print("销毁对象{0}".format(self))
p1 = Person() # 程序执行结束后,自动删除对象p1,所以又调用了def __del__()
p2 = Person() # 先手动删除了p2:销毁对象<__main__.Person object at 0x0000019BFCE9FA20>
del p2
print("程序结束")
print(p2) # NameError: name 'p2' is not defined
'''
销毁对象<__main__.Person object at 0x0000019BFCE9FA20>
程序结束
销毁对象<__main__.Person object at 0x0000019BFCC716A0>
'''
__ call __方法和可调用对象
定义了__ call __方法的对象,称为“可调用对象”,即该对象可以像函数一样被调用
class SalaryAccount:
def __call__(self, month_salary):
print("算工资")
year_salary = month_salary * 12
day_salary = month_salary // 27.5
hour_salary = day_salary // 8
return dict(year_salary=year_salary, day_salary=day_salary, hour_salary=hour_salary)
s = SalaryAccount()
print(s(3000))