Python高级篇(06):可见性和装饰器

一、可见性

  • 属性通常设置为private或protected,__name表示private属性,_name表示protected属性
  • 方法通常设置为public

1、python中以单下划线和双下滑线开头的属性和方法

1.1 以单下划线 (_) 开头的属性和方法(内部属性/方法):私有化属性或⽅法,类内部、类对象和⼦类可以访问。其他模块通过from module_name import *不能将其导入;但通过import module_name依然可以导入。

1.2 以双下划线 (__) 开头并且不以双下划线结尾的属性和方法(私有属性/方法):某个属性或方法只能在当前类内部被使用,类外部不能调用。不能调用是因为Python私有属性存储时其名字做了修改,将“__属性名”存储时重命名为"_类名__属性名"。其他模块通过from module_name import *不能将其导入;但通过import module_name依然可以导入。

2、举例

2.1 双下划线开头私有属性只能在当前类内部被访问,类外部直接访问时报错

class Student:
    def __init__(self, name, age):
        # 创建私有属性
        self.__name = name
        # 创建公共属性
        self.age = age

    def study(self, course_name):
        print("{}正在学习{}".format(self.__name, course_name))  # self.__name 只能在当前类内部被调用


stu = Student("张三", 8)
# print(stu.__name)  # 'Student' object has no attribute '__name'  私有属性类不能在类外部直接访问

#类外部间接访问私有属性:通过公共方法在类内部访问私有属性
stu.study("程序设计")  # 张三正在学习程序设计

print(stu.age)  # 8

2.2 单下划线开头的内部属性可以在类外部直接访问

class MyClass:
    def __init__(self):
        self._internal_var = "Internal Variable"
        self.__private_var = "Private Variable"

    def _internal_method(self):
        print("This is an internal method.")

    def __private_method(self):
        print("This is a private method.")

    def public_method(self):
        print("This is a public method.")
        self._internal_method()
        self.__private_method()


obj = MyClass()
print(obj._internal_var)  # 内部属性可以在类外部被直接访问。输出: Internal Variable

# print(obj.__private_var)  # 报错。原因是私有属性无法在类外部直接访问

obj.public_method()  # 输出: This is a public method.
                     #      This is an internal method.
                     #      This is a private method.

二、装饰器

  • 装饰器的作用:不修改被修饰对象源代码和调用方式前提下,为被修饰对象添加额外功能(即有新需求时,可以对现有代码进行扩展,但是不能对源代码进行修改)。
  • 装饰器的使用方法:通常放在类、函数或方法声明之前,通过@符号将装饰器应用于类、函数或方法。
  • 装饰器的使用场景:插入日志、性能测试、事务处理、缓存、权限校验等应用场景,有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。

1、无参装饰器

import time


# 定义一个装饰器
def display_time(func):  # 参数func等同于:调用装饰器后要运行哪个函数,则将哪个函数带入
    def wrapper():  # 运行这个函数时需要运行哪些内容
        t1 = time.time()  # 启一个记录初始时间的定时器
        # func()  # 运行一下装饰器中调用的函数(但是这种方法装饰器中调用的函数返回值不会传入装饰器中)
        res = func()  # 将装饰器中调用的函数返回值记录一下放在res中
        t2 = time.time()  # 启一个记录截止时间的定时器
        print(t2 - t1)
        return res  # 在装饰器中将函数的返回结果再次返回
    return wrapper


# 定义一个求质数的方法
def is_prime(num):
    if num <= 1:
        return False
    if num == 2:
        return True
    for i in range(2, num):
        if num % i == 0:
            return False
    return True


@display_time  # 等同于:count_prime_nums=display_time(count_prime_nums)
def count_prime_nums():  # 求2~10000之间的所有质数
    count1 = 0
    for i in range(2, 10000):
        if is_prime(i):
            # print(i)
            count1 += 1  # 统计质数总个数
    return count1


if __name__ == '__main__':
    c = count_prime_nums()
    print(c)

总结:

  1. python解释器解释到@display_time就会调用display_time函数。
  2. 调用display_time函数时,会将被装饰函数count_prime_nums当做实参传入,返回一个闭包函数wrapper,将wrapper赋值给函数名count_prime_nums。
  3. 调用count_prime_nums函数,执行的是wrapper(),在wrapper的函数体内再执行原始的count_prime_nums函数。
  4. 当被装饰函数count_prime_nums有返回值时,wrapper函数则需要将被装饰函数的值进行返回。如果不返回,则输出的结果就是None。

2、有参装饰器

import time


# 定义一个装饰器
def display_time(func):  # 参数func等同于:调用装饰器后要运行哪个函数,则将哪个函数带入
    def wrapper(*args):  # 运行这个函数时需要运行哪些内容
        t1 = time.time()  # 启一个记录初始时间的定时器
        # func()  # 运行一下装饰器中调用的函数(但是这种方法装饰器中调用的函数返回值不会传入装饰器中)
        res = func(*args)  # 将装饰器中调用的函数返回值记录一下放在res中
        t2 = time.time()  # 启一个记录截止时间的定时器
        print(t2 - t1)
        return res  # 在装饰器中将函数的返回结果再次返回
    return wrapper


# 定义一个求质数的方法
def is_prime(num):
    if num <= 1:
        return False
    if num == 2:
        return True
    for i in range(2, num):
        if num % i == 0:
            return False
    return True


@display_time  # 调用装饰器
def count_prime_nums(max_num):  # 求2~10000之间的所有质数
    count1 = 0
    for i in range(2, max_num):
        if is_prime(i):
            # print(i)
            count1 += 1  # 统计2~10000之前的质数总个数
    return count1


if __name__ == '__main__':
    c = count_prime_nums(2000)
    print(c)

总结:

  1. 被装饰函数func的参数是什么样子,wrapper的参数就应该什么样子
  2. func的返回值什么样子,wrapper的返回值就应该什么样子
  3. func的属性什么样子,wrapper的属性就应该什么样子 ==》from functools import wraps

3、property对象和装饰器

1.1 作用:简化访问私有属性,提供开放性接口,供外界访问。

1.2 property实现原理:属性定义为私有属性时,往往需要两个函数分别用来获取属性值(get方法)和设置属性值(set方法),使用property取代get和set方法。

"""
使用property对象修改私有属性值
property(fget=None, fset=None, fdel=None, doc=None)
说明:
fget 是获取属性值的函数。
fset 是设置(修改)属性值的函数。
fdel 是删除属性值的函数。
doc 是属性描述信息。如果省略,会把 fget 方法的文档字符串(docstring)拿来用(如果有的话)。
"""
class Test(object):
    def __init__(self):
        self.__num = 100

    # get_num函数专门用来获取__num属性值
    def get_num(self):
        return self.__num

    # set_num函数专门用来设置__num属性值    
    def set_num(self, new_num):
        self.__num = new_num

    # 定义的num就是在外面用到的属性名,property括号中前面放get方法的名字,后面放set方法的名字
    num = property(get_num, set_num)


t = Test()
t.num = 200  # 直接通过访问属性的形式来调用set_num()方法,并将等号右边的参数传进去
print(t.num)  # 通过同样的方式来调用get_num()方法
# 虽然都是t.num的方式调用方法,Python解释器会根据是否有赋值来选择调用的方法
"""
使用property装饰器修改私有属性值
被 @property 装饰的方法是获取属性值的方法,被装饰方法的名字会被用做属性名
被 @属性名.setter 装饰的方法是设置属性值的方法
被 @属性名.deleter 装饰的方法是删除属性值的方法

"""
class Test(object):
    def __init__(self):
        self.__num = 100

    # 在get方法的上面写上@property,函数的名字就是属性的名字
    @property
    def num(self):
        return self.__num

    # 在set方法的上面写上@属性名.setter,函数的名字跟get方法相同,都等于属性的名字
    @num.setter
    def num(self, new_num):
        self.__num = new_num


t = Test()
t.num = 200  # 调用了下面的num()方法进行设置
print(t.num)  # 调用了上面的num()方法获取值

在使用property时,需要了解一些细节问题:

  1. getter方法名和属性名一致(上例中是num)。
  2. setter方法名必须与getter方法名相同。
  3. 必须先定义getter方法,setter方法才能使用。 
  4. @property装饰器则是Python中的一种语法糖,它可以将一个类方法转换为一个属性,使得我们可以像访问普通属性一样调用该方法。
"""
1.属性是公有属性时,使用property,必须使用setter方法
2.若省略setter方法,此时该属性变成只读属性。如果此时仍然设置属性,会抛出异常 AttributeError: can't set attribute。
3.如果报错 RecursionError: maximum recursion depth exceeded while calling a Python object,很可能是对象属性名和 @property 装饰的方法名重名了,一般会在对象属性名前加一个下划线 _ 避免重名,并且表明这是一个受保护的属性。
"""
class Student:
    def __init__(self, name, age):
        self.name = name  
        self.age = age

    # 获取私有属性
    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value


stu = Student("张三", 8)
stu.name = "李四"
print(stu.name)  # 李四

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值