✍ 个人博客:https://blog.youkuaiyun.com/Newin2020?type=blog
📝 专栏地址:https://blog.youkuaiyun.com/newin2020/category_12801353.html
📣 专栏定位:为 0 基础刚入门 Python 的小伙伴提供详细的讲解,也欢迎大佬们一起交流~
📚 专栏简介:在这个专栏,我将带着大家从 0 开始入门 Python 的学习。在这个 Python 的新人系列专栏下,将会总结 Python 入门基础的一些知识点,方便大家快速入门学习~
❤️ 如果有收获的话,欢迎点赞 👍 收藏 📁 关注,您的支持就是我创作的最大动力 💪
1. 属性访问控制
1.1 property 装饰器
Python 中的 @property 是 python 的一种装饰器,是用来修饰方法的。
作用:
我们可以使用 @property 装饰器来创建只读属性,@property 装饰器会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改。
1.1.1 未添加 @property
class Student:
def score(self):
return 60
s = Student()
print(s.score()) # 60
1.1.2 添加了 @property
添加 @property 后,我们就可以像调用属性一样获取 score 值,但是需要注意 score 是不可修改的。
class Student:
@property
def score(self):
return 60
s = Student()
print(s.score) # 60
另外,我们也可以在 score 前面加上 _,即将 score 属性变成私有属性,这样就只能通过初始化的方式赋值,并且后续不能再修改 score 属性了。
class Student:
def __init__(self, score):
self._score = score
# 使用@property装饰器
@property
def score(self):
return self._score
p = Student(100)
print(p.score)
如果尝试修改 score 属性,则会抛出 AttributeError 的异常。若真的想修改这个只读属性,则就需要用到下面介绍的 setter 装饰器了。
p.score = 101 # 会抛出下面的异常
1.2 setter 装饰器
如果我们想对某个变量限制一个取值的范围,不想它被随意定义或恶意修改,则可以使用 setter 装饰器。一般 setter 装饰器会和 property 装饰器一起出现,使用 setter 后,被 property 限定的只读属性会变成可读可写的属性。
例如,我现在定义的 Student 类中存在一个 score 的属性,当我加上 property 装饰器后 score 理应会变成只读的属性,但我现在又加上了 setter 装饰器,那么 score 属性就变得可读可写了。
class Student:
def __init__(self, score):
self.score = score
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError("score must be an interger!")
if value < 0 or value > 100:
raise ValueError("score must between 0 ~ 100!")
self._score = value
另外,我在 setter 下的函数中设置了 score 属性的取值范围,如果修改的值没有满足相关规定,则会抛出相应的异常。下面可以看看 score 属性修改成不同的值,结果会如何。
- score 修改值在规定范围内(不会抛出异常)
s = Student(60)
s.score = 100
- score 修改值不是整数(抛出异常)
s = Student(100)
s.score = "123"
- score 修改值不在 0 ~ 100 之间(下面两段代码结果相同,会抛出同种异常)
# 代码一
s = Student(101)
# 代码二
s = Student(100)
s.score = 101
Tips:
无论是使用 property 控制只读还是 setter 控制可写,它们下面的方法名(要控制的属性)都要保持一致。
2. 多个装饰器执行顺序
一个函数还可以同时定义多个装饰器,比如:
@a
@b
@c
def func():
pass
它的执行顺序是从里到外,最小调用最里层的装饰器,最后调用最外层的装饰器,它等效于:func = a(b(c(func)))
。
我们直接上一个具体的案例来理解一下。
def func1(f):
def inner_func1():
print("enter inner_func1")
f() # 最原始的func_1
print("exit inner_func1")
return inner_func1
def func2(f):
def inner_func2():
print("enter inner_func2")
f() # 等价于func1(func_a)()=inner_func1()
print("exit inner_func2")
return inner_func2
@func2 # 等价于func_a = func2(func1(func_a)) = inner_func2
@func1 # 等价于func_a = func1(func_a) = inner_func1
def func_a():
print("hello world")
func_a() # 等价于inner_func2()
根据输出的结果可以发现,这有点 C++ 类继承中构造函数和析构函数的味道,或者通俗来讲就是像穿秋裤一样,先穿后脱,先外后内。
接下来我们再加上两行代码,看看打印的结果又如何。
def func1(f):
print("enter func1")
def inner_func1():
print("enter inner_func1")
f() # 最原始的func_1
print("exit inner_func1")
return inner_func1
def func2(f):
print("enter func2")
def inner_func2():
print("enter inner_func2")
f() # 等价于func1(func_a)()=inner_func1()
print("exit inner_func2")
return inner_func2
@func2 # 等价于func_a = func2(func1(func_a)) = inner_func2
@func1 # 等价于func_a = func1(func_a) = inner_func1
def func_a():
print("hello world")
func_a() # 等价于inner_func2()
从输出结果可以发现,func 函数内且 inner_func 函数外的部分会先执行。
3. 迭代器
3.1 可迭代对象
- 迭代:使用 for 循环遍历取值的过程叫做迭代。例如,使用 for 循环便利列表获取值的过程。
- 迭代对象:使用 for 循环遍历取值的对象叫做迭代对象。例如,列表、元组、字典、集合、range、字符串。
如果想判断一个对象是否是可迭代对象,则可以使用 isinstance 函数进行判断。
from collections.abc import Iterable
a = [1, 2, 3]
print(isinstance(a, Iterable)) # True
print(isinstance((), Iterable)) # True
print(isinstance({}, Iterable)) # True
print(isinstance("hello world", Iterable)) # True
print(isinstance(100, Iterable)) # False
3.2 迭代器详解
3.2.1 迭代器和迭代器对象
- 迭代器是一个可以记住遍历的位置的对象。
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束,迭代器只能往前不会后退。
迭代器有两个基本的方法:iter() 和 next(),字符串、列表或元组对象都可用于创建迭代器,迭代器对象可以使用常规 for 语句进行遍历,也可以使用 next() 函数来遍历。
list、tuple 等都是可选代对象,可以通过 iter() 函数获取这些可选代对象的迭代器。然后我们可以对获取到的迭代器不断使用 next() 函数来获取下一条数据。iter() 函数实际上就是调用了可迭代对象的 iter 方法。next() 实际调用了 next() 方法。
a = [1, 2, 3]
it = iter(a)
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
3.2.2 自定义迭代器
我们也可以自定义一个迭代器,并可以实现自己的遍历逻辑。
例如下面这个例子,我通过自己实现了一个迭代器并重新定义了 next 方法,让遍历的步长设置为 2,且遍历到 100 就停止,则代码下面就会输出 0 ~ 100 的偶数。
class Sequence:
def __init__(self):
self.num = 0
def __iter__(self):
return self
def __next__(self):
val = self.num
if val >= 100:
raise StopIteration
self.num += 2
return val
test = Sequence()
for i in test:
print(i)
而 range 函数其实和上面的实现方法类似,也是通过自己定义迭代器来实现不同的逻辑。
3.2.3 可迭代对象和迭代器对象的区别
- 可迭代对象
- Iterable:可迭代对象,继承迭代器对象,必须要实现 iter 方法,可以用 for 循环
- 只要可以用作 for 循环的都是可选代对象
- 迭代器对象
- Iterator:选代器对象,必须要实现 next 方法,可以用 next 获取下一个值,但是每个值只能获取一次
- 只要可以用 next() 函数的都是迭代器对象
列表、字典、字符串是可迭代对象但并不一定是迭代器对象,如果想变成迭代器对象可以使用 iter() 进行转换。