一、可见性
- 属性通常设置为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)
总结:
- python解释器解释到@display_time就会调用display_time函数。
- 调用display_time函数时,会将被装饰函数count_prime_nums当做实参传入,返回一个闭包函数wrapper,将wrapper赋值给函数名count_prime_nums。
- 调用count_prime_nums函数,执行的是wrapper(),在wrapper的函数体内再执行原始的count_prime_nums函数。
- 当被装饰函数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)
总结:
- 被装饰函数func的参数是什么样子,wrapper的参数就应该什么样子
- func的返回值什么样子,wrapper的返回值就应该什么样子
- 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时,需要了解一些细节问题:
- getter方法名和属性名一致(上例中是num)。
- setter方法名必须与getter方法名相同。
- 必须先定义getter方法,setter方法才能使用。
- @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) # 李四