getattr()函数、setattr()函数

getattr(object, name[, default])

getattr(object, name[, default])

getattr简介

参数

  • object – 对象。
  • name – 字符串,对象属性。
  • default – 默认返回值,如果不提供该参数,在没有对应属性时,将触发 AttributeError。

返回值

返回对象属性值。

 

例如

class Person(NamedTuple):
    '''人类'''
    name: str
    age: int
    job: str
    weight: float
    height: float

 

alex对象有name,age,job,weight,height五个属性,我们要根据用户的选择来展示相应的属性。也就是说,用户告诉程序“给我看看alex的身高吧!”,程序就要把height属性的值展示给用户看。

当无法确定要访问哪个属性值,就用getattr(alex, "age"),,而不是直接用alex.age

用getattr就非常合适了。因为,getattr的第二个参数类型正是字符串!我们可以用getattr(alex,“height”)来取出身高的值。我们甚至还可以使用default参数很好地解决用户输入的属性不存在的问题。

from typing import NamedTuple
 
class Person(NamedTuple):
    '''人类'''
    name: str
    age: int
    job: str
    weight: float
    height: float
 
alex = Person('Alex', 32, 'actor', 60, 178)
 
# 把用户输入的字符串赋值给变量attribute_name
attribute_name = input('''What do you want to know about Alex? 
Enter an attribute name>>>''')
# 注意,上述字符串被传进了这个函数作为第二个参数
# 第三个参数是属性不存在时返回的字符串
print(getattr(alex,attribute_name, 'Sorry, this attribute does not exist.'))

 

=========================

getattr() 详解

getattr() 可以通过对象实例或类名来获取属性值或方法,也可以获取内置函数、内置类型和标准库中的属性和方法。

1.获取对象属性值

class MyClass:
    def __init__(self):
        self.x = 1
        self.y = 2


obj = MyClass()

print(getattr(obj, 'x'))
print(getattr(obj, 'y'))

 2.获取对象的方法

class MyClass:
    def my_method(self):
        print('Hello, world!')


obj = MyClass()

method = getattr(obj, 'my_method')
method()

 3.获取内置函数和类型

func = getattr(__builtins__, 'abs')
print(func(-1))

 

type_name = 'str'
type_obj = getattr(__builtins__, type_name)
print(type_obj('Hello, world!')) 

 4.获取标准库中的属性和方法

import datetime

now = datetime.datetime.now()

attr_name = 'year'
attr_value = getattr(now, attr_name)
print(attr_value)  # 输出当前年份

method_name = 'strftime'
method_args = ['%Y-%m-%d %H:%M:%S']
method = getattr(now, method_name)
formatted = method(*method_args)
print(formatted)  # 输出时间

 

复杂案例

getattr() 还可以结合 import_module() 函数,实现动态执行某个文件中某个类的方法的功能。

下面是一个稍微复杂一些的例子。

假设我们有一个 data.py 文件,其中定义了一个 Data 类和一些数据操作方法,代码如下:

# data.py
class Data:
    def __init__(self, data):
        self.data = data

    def get(self):
        return self.data

    def add(self, value):
        self.data.append(value)

    def remove(self, value):
        self.data.remove(value)


def load_data():
    return [1, 2, 3]

现在我们需要在另一个模块中,动态加载 data.py 文件,并使用 Data 类的实例对象进行数据操作,同时也需要使用 load_data() 函数进行数据的读操作。

import importlib

module_name = 'data'
class_name = 'Data'
method_name = 'get'

module = importlib.import_module(module_name)
class_obj = getattr(module, class_name)(module.load_data())
method = getattr(class_obj, method_name)
print(method())  # 输出 [1, 2, 3]

value = 4
method_name = 'add'
method = getattr(class_obj, method_name)
method(value)

method_name = 'get'
method = getattr(class_obj, method_name)
print(method())  # 输出 [1, 2, 3, 4]

value = 2
method_name = 'remove'
method = getattr(class_obj, method_name)
method(value)

method_name = 'get'
method = getattr(class_obj, method_name)
print(method())  # 输出 [1, 3, 4]

 

在上述示例中,我们首先使用 import_module() 函数导入了 data.py 文件,然后使用 getattr() 函数获取了 Data 类的对象。接着,我们使用 () 运算符执行了 get()、add() 和 remove() 方法,从而动态进行了数据操作。

这个例子中,我们动态地加载了一个 Python 模块,并使用 getattr() 函数获取了模块中的类和函数对象。然后,我们使用这些对象来动态地进行数据操作。这种动态加载和执行的方式,可以使代码更加灵活和可扩展
import_module介绍

import_module() 函数得到的是一个模块对象,即一个表示 Python 模块的对象。模块对象是一个包含模块中所有定义的类、函数、变量等的命名空间,我们可以使用模块对象来访问这些定义。

例如,我们可以使用 module 对象来访问 data.py 文件中定义的 Data 类和 load_data() 函数,代码如下:

import importlib

module_name = 'data'

module = importlib.import_module(module_name)
data = module.Data([1, 2, 3])
loaded_data = module.load_data()
print(loaded_data)

在上述代码中,我们首先使用 import_module() 函数导入了 data.py 文件,然后通过 module 对象访问了 Data 类和 load_data() 函数。我们还使用 () 运算符创建了 Data 类的实例对象 data,并调用了 load_data() 函数,获取了数据列表 loaded_data。

关于加了括号后相当于调用了相应的方法,这是因为在 Python 中,函数和方法都是可调用对象。我们可以通过使用 () 运算符来调用函数和方法。

例如,在上述代码中,我们使用 () 运算符调用了 Data 类的构造函数和 load_data() 函数,从而创建了 data 对象和获取了 loaded_data 数据。这与直接调用类或函数的方式是相同的,例如:

data = Data([1, 2, 3])
loaded_data = load_data()
print(loaded_data)

在这种情况下,我们直接调用了类和函数,并通过它们来创建了对象和获取了数据,而不需要使用模块对象来访问它们。

====================================

setattr(object, name, values)

class test():
    name="xiaohua"
    def run(self):
        return "HelloWord"


t = test()
y0 = hasattr(t, "age")  # 判断属性是否存在
print("属性age是否存在:")
print(y0)

y1 = setattr(t, "age", "18")  # 为属性赋值,且没有返回值
print(y1)


y00 = hasattr(t, "age")  # 再次判断属性是否存在
print("赋值后,属性age是否存在:")
print(y00)

 综合运用:

class test():
    name = "xiaohua"
    def run(self):
        return "HelloWord"


t = test()
# getattr(t, "age")  # age属性不存在,运行会报错

a = getattr(t, "age", setattr(t, "age", "18"))  # age属性不存在时,设置该属性
print(a)

b = getattr(t, "age")  #可检测设置成功
print(b)

 

__getattribute__()

在Python中自定义了很多内建属性,如__getattribute__(),即属性访问拦截器,它的作用是当我们访问实例对象的属性时,并不会立即返回属性的值,而是自动调用__getattribute__()方法,并将它的返回结果作为属性值。(注意是实例对象属性,类属性访问不会调用__getattribute__方法,而是直接由类名.__dict__[属性名]获取)

值得一提的在Python中对实例对象属性的访问有如下顺序:

__getattribute__ --> 数据描述符--> 实例属性 --> 类属性,非数据描述器 --> __getattr__

(1)判断实例对象所属(attr)的类是否重写了__getattribute__(),如果定义了则调用,将调用结果作为属性值返回

(2)若是未显示定义__getattribute__(),则在对象的__dict__中查找属性(若该属性是python自动产生的则直接返回)

(3)若不是由python直接产生的,则看其是否是描述符属性,若是描述符属性(且为数据描述符)则由描述符的__get__方法的返回值作为属性值,若是非数据描述符
则看当前对象的__dict__中是否有和父类类属性中描述符属性同名属性,若当前实例对象中有则直接返回该值,若没有,则由父类描述符的__get__方法返回
值作为属性值;

(4)若是在自身的__dict__中未找到该属性,则到父类中查找,并重复2,3步骤,仍未找到则触发__getattr__抛出AttributeError

从中从上不难看出__getattribute__ 和__getattr__方法的重要性。

实现__getattribute__方法与__getattr__:

class A:
    demo = 'PYTHON'
    def __init__(self, des, title):
        self.des = des
        self.title = title
 
    def __getattribute__(self, item):
        """重写__getattribute__方法,实现拦截功能"""
        if item == 'des':
            return "you can't acess this "
        else:
            return 'no such attr'
 
a = A('python __getattribute__', 'python')
 
print(a.des) # 访问实例属性des
>> you can't access this
print(a.demo) # 通过实例对象访问类属性demo
>> no such attr
print(a.xxx) # 访问不存在的属性
>> no such attr
print(A.demo) # 通过类名直接访问类属性
>> PYTHON

从上不难发现当我们通过实例对象访问属性时,都会自动触发定义的__getattribute__方法对我们的行为进行拦截,而拿不到真正的属性,但是通过类直接访问时却不会触发(此外内置getattr和hasattr也会触发这个方法__getattribute__的调用)。

在重写了__getattribute__()方法之后,我们如何拿到真正想要的属性的值呢?----只需在__getattribute__中调用超类的__getattribute__方法即可

class A:
    demo = 'PYTHON'
    def __init__(self, des, title):
        self.des = des
        self.title = title
 
    def __getattribute__(self, item):
        """重写__getattribute__方法,实现拦截功能"""
        if item == 'des':
            return "you can't access this "
        else:
            return object.__getattribute__(self, item)
 
            # 或者super().__getattribute__(item)
            # 注意此处不能使用self.item 来获取,否则会无限递归调用,也就是说我们可以直接
            # 使用基类的__getattribute__方法获取实例属性的值
            # object.__getattribute__(a, 'des') --->python __getattribute__
 
a = A('python __getattribute__', 'python')
 
print(a.des) # 访问实例属性des
>> you can't access this
print(a.demo) # 通过实例对象访问类属性demo,获取到了真正的属性
>> PYTHON

从前面可知当访问属性过程中使用__getattribute__方法未找到所需要的属性时会触发__getattr__,但是当我们重写了__getattribute__方法之后,是否还会触发调用?如下:

class A:
    demo = 'PYTHON'
    def __init__(self, des, title):
        self.des = des
        self.title = title
 
    def __getattribute__(self, item):
        """重写__getattribute__方法,实现拦截功能"""
        if item == 'des':
            return "you can't access this "
        else:
            return "_*_*_*_*"
 
    def __getattr__(self, item):
        return  'no such attr !!!'
 
 
a = A('python __getattribute__', 'python')
 
 
print(a.xxx) # 访问不存在的属性
>> _*_*_*_*

当访问的属性不存在时,并没有执行__getattr__方法,也就是__getattribute__的优先级更高,如果想要__getattr__方法也执行,则需要显示调用它或者在__getattribute__中抛出异常,如下:

class A:
    demo = 'PYTHON'
    def __init__(self, des, title):
        self.des = des
        self.title = title
 
    def __getattribute__(self, item):
        """重写__getattribute__方法,实现拦截功能"""
        if item == 'des':
            raise AttributeError
        else:
            return object.__getattribute__(self, item)
 
    def __getattr__(self, item):
        return  'no such attr !!!'
 
 
a = A('python __getattribute__', 'python')
 
print(a.des) # 手动抛出AttributeError时也会触发调用__getattr__方法
>> no such attr !!!
 
print(a.xxx) # 访问不存在属性时,由object.__getattribute__(self, item)自动抛出异常从而
             # 触发调用__getattr__方法
>> no such attr !!!  

 

posted @ 2023-09-22 21:25  yimu-yimu  阅读(174)  评论(0编辑  收藏  举报