【新人系列】Python 入门(二十):装饰器 - 下

✍ 个人博客: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() 进行转换。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值