Python-VBA函数之旅-setattr函数

news2024/11/27 15:34:34

目录

一、setattr函数的常见应用场景

二、setattr函数使用注意事项

三、如何用好setattr函数?

1、setattr函数:

1-1、Python:

1-2、VBA:

2、推荐阅读:

个人主页: https://blog.csdn.net/ygb_1024?spm=1010.2135.3001.5421

一、setattr函数的常见应用场景

        setattr函数在Python中有广泛的应用场景,特别是在动态实现类属性或方法、元编程(metaprogramming)以及处理对象时不知道具体属性名的情况下,常见的应用场景有:

1、动态实现类属性或方法:当你不确定类的所有属性或方法时,可以在运行时使用setattr()函数动态地添加它们,这可以用于创建可扩展的类,或者根据外部配置动态地添加功能。

2、简化对象属性的批量设置:如果你有一个对象,并且需要从字典或其他数据源中设置多个属性,setattr()函数可以帮助你简化这个过程。

3、实现代理对象或装饰器:在代理对象或装饰器模式中,你可能需要拦截对某个对象的属性访问,并在访问之前或之后执行一些操作,你可以使用setattr()函数来动态地修改被代理对象的属性。

4、结合描述符(Descriptors)使用:描述符是Python中一个强大的特性,允许你控制对对象属性的访问,当描述符与setattr()函数结合使用时,你可以实现更复杂的属性访问逻辑。

5、序列化和反序列化:在处理自定义对象的序列化和反序列化时,setattr()函数可以帮助你根据序列化的数据动态地设置对象的属性,这在处理JSON、XML或其他格式的数据时特别有用。

6、元编程:在元编程中,你编写操作其他代码的代码,setattr()函数允许你动态地修改对象的结构,这在创建代理、装饰器、元类等高级编程概念时非常有用。

7、处理用户输入:如果你正在编写一个接受用户输入并动态设置对象属性的程序,setattr()函数可以帮助你根据用户输入来设置属性。

        注意,过度使用setattr()函数可能会使代码难以理解和维护,因为它破坏了对象的明确性和封装性,因此,在使用setattr()函数时应该谨慎,并确保你的代码清晰、可读和可维护。

二、setattr函数使用注意事项

        在Python中,setattr()是一个内置函数,用于设置对象的属性值,它的基本用法是setattr(object, name, value),其中,object是你想要修改其属性的对象,name是属性的名称(作为一个字符串),而value是你想要设置的新值。

在使用setattr()函数时,需注意以下事项:

1、属性名称作为字符串:name参数必须是一个字符串,它指定了你想要设置的属性的名称,如果你尝试使用一个非字符串值,Python会抛出一个TypeError异常。
2、不存在的属性:如果对象没有名为name的属性,setattr()会创建一个新的属性,但是,如果你试图设置一个只读属性(如某些内置类型或扩展类型中的属性),可能会引发异常。
3、隐藏或覆盖内置方法:如果你使用setattr()函数设置了一个与对象现有方法同名的属性,那么这个方法将被该属性隐藏或覆盖,这可能会导致意外的行为,特别是如果后来你尝试调用那个方法但忘记了已经被覆盖。
4、动态属性:虽然setattr()函数允许你动态地设置对象的属性,但这并不意味着你应该滥用它,过度使用动态属性可能会使代码难以理解和维护,在可能的情况下,最好明确地在类定义中声明属性。
5、安全性:setattr()函数可以被用于修改对象的任何属性,包括私有属性(在Python中,以双下划线开头的属性名称被认为是私有的),这可能会破坏对象的内部状态,并导致不可预测的行为,因此,在使用setattr()函数时,你应该非常小心,确保你只修改你真正想要修改的属性。
6、异常处理:在设置属性时,可能会发生各种异常,如AttributeError(如果尝试访问不存在的属性)或TypeError(如果属性类型与赋值不兼容),你应该准备好处理这些异常,以防止程序崩溃。
7、性能:虽然setattr()函数在大多数情况下都足够快,但在某些情况下,频繁地使用它可能会导致性能问题,如果你需要频繁地设置大量属性,可能需要考虑其他更高效的方法。
8、结合getattr()/delattr()使用:getattr()/delattr()是另外两个与setattr()相关的内置函数,它们分别用于获取和删除对象的属性,在编写处理对象属性的代码时,这三个函数通常会一起使用。

三、如何用好setattr函数?

        要在Python中妥善使用setattr()函数,你需遵循以下建议:

1、明确目的:首先,确定你为何需要使用setattr()函数?在大多数情况下,直接在类定义中声明属性或使用常规的赋值语法是更简单、更直观的选择,如果你确实需要动态地设置属性,那么setattr()函数是一个有用的工具。

2、避免覆盖内置方法:在调用setattr()函数时,确保你没有意外地覆盖对象的内置方法或属性,你可以通过检查name参数的值来避免这种情况。

3、考虑封装:如果你经常需要动态地设置属性,可以考虑将setattr()的调用封装到一个类或方法中,这样可以隐藏底层复杂性,并提供一个更清晰的接口给调用者。

4、使用字典代替属性:如果你需要存储大量的动态属性,并且不关心它们作为对象的属性暴露给外部代码,那么使用字典来存储这些属性可能是一个更好的选择。字典提供了灵活的键值对存储,并且你可以使用dict.get(), dict.setdefault() 和dict.update()等方法来操作它们。

5、处理异常:当使用setattr()函数时,要准备好处理可能发生的异常,如AttributeError(如果尝试访问不存在的属性)或TypeError(如果赋值类型不兼容),你可以使用try/except块来捕获这些异常,并相应地处理它们。

6、文档化:如果你在你的代码中使用setattr()函数,确保在相关的文档或注释中解释清楚它的用途和潜在的影响,这可以帮助其他开发者更好地理解你的代码,并避免意外的副作用。

7、测试:编写测试用例来验证你使用setattr()函数的代码的正确性。确保你的测试覆盖了各种情况,包括正常情况和异常情况,这可以帮助你发现潜在的问题,并确保你的代码在所有预期的情况下都能正常工作。

8、谨慎使用:尽管setattr()函数在某些情况下很有用,但它也可能导致代码难以理解和维护,因此,你应该谨慎地使用它,并确保你的代码在不需要动态属性时仍然能够正常工作。

1、setattr函数:
1-1、Python:
# 1.函数:setattr
# 2.功能:用于设置对象的属性值
# 3.语法:setattr(object, name, value)
# 4.参数:
# 4-1、object:对象
# 4-2、name:字符串,表示对象属性
# 4-3、value:属性值
# 5.返回值:无
# 6.说明:
# 7.示例:
# 用dir()函数获取该函数内置的属性和方法
print(dir(setattr))
# ['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
# '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__name__',
# '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__',
# '__str__', '__subclasshook__', '__text_signature__']

# 用help()函数获取该函数的文档信息
help(setattr)

# 应用一:动态实现类属性或方法
# 示例1:动态设置类实例的属性
class MyClass:
    def __init__(self):
        self.existing_attribute = 'I exist!'
# 创建一个类的实例
instance = MyClass()
# 使用setattr()动态地添加一个属性
setattr(instance, 'my_weight', '70.6kg')
# 访问动态添加的属性
print(instance.my_weight)
# 70.6kg

# 示例2:动态设置类的方法
class MyClass:
    def existing_method(self):
        print('I am an existing method!')
# 创建一个类的实例
instance = MyClass()
# 定义一个要动态添加的方法
def dynamic_method(self):
    print('I am a dynamically added method!')
# 使用setattr()动态地添加一个方法
setattr(MyClass, 'dynamic_method', dynamic_method)
# 在类的实例上调用动态添加的方法(注意:我们是在类上添加的方法,所以所有实例都可以访问)
instance.dynamic_method()
# I am a dynamically added method!

# 示例3:动态设置类的属性(即类变量)
class MyClass:
    existing_class_variable = 'I am a class variable!'
# 使用setattr()动态地添加一个类变量
setattr(MyClass, 'dynamic_class_variable', 'I was dynamically added as a class variable!')
# 访问动态添加的类变量(通过类本身或其实例)
print(MyClass.dynamic_class_variable)  # 输出: I was dynamically added as a class variable!
print(MyClass().dynamic_class_variable)  # 同样输出: I was dynamically added as a class variable!
# I was dynamically added as a class variable!
# I was dynamically added as a class variable!

# 应用二:简化对象属性的批量设置
class Person:
    def __init__(self):
        self.name = None
        self.age = None
        self.city = None
# 创建一个Person实例
person = Person()
# 使用字典来存储要设置的属性值
attributes = {
    'name': 'Myelsa',
    'age': 18,
    'city': 'Guangzhou'
}
# 使用setattr()批量设置属性
for key, value in attributes.items():
    setattr(person, key, value)
# 验证属性是否设置成功
print(person.name)
print(person.age)
print(person.city)
# Myelsa
# 18
# Guangzhou

# 应用三:实现代理对象或装饰器
class Proxy:
    def __init__(self, obj):
        self._obj = obj
    def __setattr__(self, name, value):
        # 检查是否是要拦截的属性
        if name.startswith('_'):
            # 如果以'_'开头,则直接设置到代理对象本身
            super().__setattr__(name, value)
        else:
            # 否则,拦截对_obj属性的设置,并可能添加一些额外的逻辑
            print(f"Intercepted setattr for {name}. Original value: {getattr(self._obj, name, 'None')}")
            # 在这里可以添加一些逻辑,比如验证、转换等
            # ...
            # 设置值到_obj
            setattr(self._obj, name, value)
            print(f"Set {name} to {value} in the original object.")
    def __getattr__(self, name):
        # 如果访问的属性不存在于代理对象本身,则转发到_obj
        return getattr(self._obj, name)
class MyClass:
    def __init__(self):
        self.x = 10
# 创建一个MyClass实例和一个代理对象
obj = MyClass()
proxy = Proxy(obj)
# 尝试设置代理对象的属性
proxy.x = 20
# 尝试访问代理对象的属性
print(proxy.x)
# Intercepted setattr for x. Original value: 10
# Set x to 20 in the original object.
# 20

# 应用四:结合描述符(Descriptors)使用
class BoundedDescriptor:
    def __init__(self, name, lower=None, upper=None):
        self.name = name
        self.lower = lower
        self.upper = upper
        self._value = None
    def __get__(self, instance, owner):
        if instance is None:
            return self
        return self._value
    def __set__(self, instance, value):
        if self.lower is not None and value < self.lower:
            raise ValueError(f"{self.name} must be greater than or equal to {self.lower}")
        if self.upper is not None and value > self.upper:
            raise ValueError(f"{self.name} must be less than or equal to {self.upper}")
        self._value = value
        print(f"Setting {self.name} to {value}")
    def __delete__(self, instance):
        raise AttributeError("Cannot delete the descriptor")
class MyClass:
    x = BoundedDescriptor('x', 0, 10)
# 创建一个MyClass的实例
obj = MyClass()
# 使用setattr()设置属性(这实际上是调用描述符的__set__方法)
setattr(obj, 'x', 5)  # 输出: Setting x to 5
print(obj.x)  # 输出: 5
# 尝试设置一个超出范围的值
try:
    setattr(obj, 'x', -1)
except ValueError as e:
    print(e)  # 输出: x must be greater than or equal to 0
# 尝试删除描述符(会引发异常)
try:
    delattr(obj, 'x')
except AttributeError as e:
    print(e)  # 输出: Cannot delete the descriptor
# Setting x to 5
# 5
# x must be greater than or equal to 0
# Cannot delete the descriptor

# 应用五:序列化和反序列化
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __repr__(self):
        return f"Person(name={self.name}, age={self.age})"
def serialize_person(person):
    """将Person对象序列化为字典"""
    return {
        'name': person.name,
        'age': person.age
    }
def deserialize_person(data):
    """从字典反序列化为Person对象"""
    person = Person(None, None)  # 创建一个空的Person对象
    for key, value in data.items():
        setattr(person, key, value)  # 使用setattr()设置属性
    return person
# 示例用法
# 序列化
person = Person("Myelsa", 18)
serialized_data = serialize_person(person)
print(serialized_data)
# 反序列化
deserialized_person = deserialize_person(serialized_data)
print(deserialized_person)
# {'name': 'Myelsa', 'age': 18}
# Person(name=Myelsa, age=18)

# 应用六:元编程
class DynamicClass:
    def __init__(self):
        # 用于存储动态方法的字典
        self._dynamic_methods = {}
    def add_method(self, name, func):
        """动态地添加一个方法到类中"""
        setattr(self, name, func)
        # 同时将方法存储在字典中以便后续可能的操作
        self._dynamic_methods[name] = func
    def remove_method(self, name):
        """动态地移除一个方法"""
        if hasattr(self, name):
            delattr(self, name)
            if name in self._dynamic_methods:
                del self._dynamic_methods[name]
    def list_methods(self):
        """列出所有的动态方法(不包括内置方法)"""
        return list(self._dynamic_methods.keys())
# 示例函数
def hello_world():
    print("Hello, world!")
def greet(name):
    print(f"Hello, {name}!")
# 创建一个DynamicClass的实例
dynamic_instance = DynamicClass()
# 动态地添加一个方法
dynamic_instance.add_method('hello', hello_world)
# 调用动态添加的方法
dynamic_instance.hello()  # 输出: Hello, world!
# 列出所有的动态方法
print(dynamic_instance.list_methods())  # 输出: ['hello']
# 动态地添加另一个方法,这次使用lambda表达式
dynamic_instance.add_method('greet_by_name', lambda name: greet(name))
# 调用通过lambda添加的方法
dynamic_instance.greet_by_name('Myelsa')  # 输出: Hello, Myelsa!
# 移除一个方法
dynamic_instance.remove_method('hello')
# 再次列出所有的动态方法
print(dynamic_instance.list_methods())  # 输出: ['greet_by_name']
# 尝试调用已被移除的方法
# dynamic_instance.hello()  # 这将引发 AttributeError
# Hello, world!
# ['hello']
# Hello, Myelsa!
# ['greet_by_name']

# 应用七:处理用户输入
class UserDefinedAttributes:
    def __init__(self):
        # 初始化一个空字典来存储用户定义的属性
        self._user_attributes = {}
    def set_attribute(self, name, value):
        # 检查属性名是否以"user_"开头,以限制可设置的属性范围
        if name.startswith("user_"):
            # 使用setattr()来设置属性
            setattr(self, name, value)
            # 同时将属性存储在字典中,以便后续可能的操作或检查
            self._user_attributes[name] = value
            print(f"Attribute {name} set to {value}")
        else:
            print(f"Error: Cannot set attribute {name}. Only attributes starting with 'user_' are allowed.")
    def get_attribute(self, name):
        # 检查属性是否存在
        if hasattr(self, name):
            return getattr(self, name)
        else:
            print(f"Error: Attribute {name} does not exist.")
            return None
    def list_attributes(self):
        # 列出所有用户定义的属性
        return list(self._user_attributes.keys())
# 示例用法
obj = UserDefinedAttributes()
# 获取用户输入
name = input("Enter attribute name (must start with 'user_'): ")
value = input("Enter attribute value: ")
# 尝试设置属性
obj.set_attribute(name, value)
# 列出所有用户定义的属性
print("User defined attributes:")
for attr in obj.list_attributes():
    print(f"{attr}: {obj.get_attribute(attr)}")
# 尝试获取并打印一个属性
attr_to_get = input("Enter attribute name to get (or 'exit' to quit): ")
if attr_to_get.lower() != 'exit':
    print(f"Value of {attr_to_get}: {obj.get_attribute(attr_to_get)}")
else:
    print("Exiting...")
1-2、VBA:
略,待后补。
2、推荐阅读:

2-1、Python-VBA函数之旅-round()函数

Python算法之旅:Algorithm

Python函数之旅:Functions

个人主页: 神奇夜光杯-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1659532.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

宏的优缺点?C++有哪些技术替代宏?(const)权限的平移、缩小

宏的优缺点&#xff1f; 优点&#xff1a; 1.增强代码的复用性。【减少冗余代码】 2.提高性能&#xff0c;提升代码运行效率。 缺点&#xff1a; 1.不方便调试宏。&#xff08;因为预编译阶段进行了替换&#xff09; 2.导致代码可读性差&#xff0c;可维护性差&#xff0…

小阳的戒S笔记

文章目录 写在前面2024年5月8日21:12:172024年5月9日21:48:242024年5月10日08:04:141、记录昨夜之身体变化2、自身制定之计划1.此亦乃要事&#xff0c;特定问了度娘与GPT&#xff0c;找时间还得咨询专业医师。2.通过跑步宣泄&#xff0c;同时锻炼身体3.我不会有压力&#xff0c…

替换spring-boot中的组件版本

spring-boot是一个用于简化开发的框架&#xff0c;引入spring-boot后会自动包含spring框架&#xff0c;通过引入xxx-start来完成指定组件的功能。比如&#xff1a; spring-boot-starter-web(嵌入 Tomcat 和 web 开发需要的 servlet 和 jsp 支持)spring-boot-starter-data-jpa(…

逻辑卷管理-LVM

目录 1. LVM的基本概念 2. Linux下创建和管理LVM 3. 环境准备 4. 物理卷管理 4.1. 创建物理卷 4.2. 显示物理卷 4.3. 删除物理卷 4. 卷组管理 4.1. 创建卷组 4.2. 显示卷组 4.3. 扩展卷组 4.4. 缩减卷组 4.5. 删除卷组 4.6. 分割卷组 4.7 组合卷组 5. 逻辑卷管…

VisualGDB:Linux静态库项目创建、编译及库的使用

接上篇《VisualGDB&#xff1a;Linux动态库项目创建、编译及库的使用》&#xff0c;静态库的创建和使用与动态库基本无差别&#xff0c;唯一需要做的就是指定项目生成静态库。 一、指定项目生成静态库 二、重新构建和编译项目 这里注意&#xff0c;同样要copy一个libxxx.so格式…

InternLM-Chat-7B部署调用-个人记录

一、环境准备 pip install modelscope1.9.5 pip install transformers4.35.2 二、下载模型 import torch from modelscope import snapshot_download, AutoModel, AutoTokenizer import os model_dir snapshot_download(Shanghai_AI_Laboratory/internlm-chat-7b, cache_di…

果味碳酸饮料二氧化碳气容量检测技术的创新与发展

果味碳酸饮料二氧化碳气容量检测技术的创新与发展 一、引言 随着健康饮食理念的普及和消费者对高品质饮料需求的增加&#xff0c;果味碳酸饮料的市场需求日益增长。在这一背景下&#xff0c;如何确保果味碳酸饮料的品质和口感成为了业界关注的焦点。二氧化碳气容量作为影响果味…

数据链路层之 以太网协议

以太网协议 这个协议即规定了数据链路层&#xff0c;同时也规定了物理层的内容。平时使用到的网线&#xff0c;其实也叫做“以太网线”&#xff08;遵守以太网协议的网线&#xff09;。 以太网帧格式 以太网数据帧 帧头 载荷 帧尾。 帧头&#xff1a;目的地址、源地址、类型…

Leetcode—232. 用栈实现队列【简单】

2024每日刷题&#xff08;131&#xff09; Leetcode—232. 用栈实现队列 实现代码 class MyQueue { public:MyQueue() {}void push(int x) {st.push(x);}int pop() {if(show.empty()) {if(empty()) {return -1;} else {int ans show.top();show.pop();return ans;}} else {i…

图像处理(二)

图像处理&#xff08;2&#xff09; 裁剪图片 from skimage import io,dataiimg io.imread(rD:\工坊\图像处理\十个勤天2.png)roiiimg[50:150,120:200,:]io.imshow(roi) 运行结果&#xff1a; 将图片进行二值化 from skimage import io,data,colorimg io.imread(r"…

TPB-1W 系列——1W 3KVDC 隔离 单输出 DC/DC 电源模块

TPB-1W系列产品是专门针对PCB上分布式电源系统中需要与输入电源隔离且输出精度要求较高的电源应用场合而设计。该产品适用于&#xff1b;1&#xff09;输入电源的电压变化≤5%&#xff1b;2&#xff09;输入输出之前要求隔离电压≥3000VDC&#xff1b;3&#xff09;对输出电压稳…

请求响应里面的日期参数

日期参数 需要在控制类使用DateTimeFormat注解 package com.ming.controller; ​ ​ import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.Rest…

【LSTM】LSTM网络及参数学习笔记

图1 LSTM模型结构可视化 [6]. 图2 LSTM cell结构说明 图3 LSTM cell和num_units说明 [4]. 图4 LSTM的网络结构 1. LSTM 是对一个LSTM层的抽象&#xff0c;可以看成是由多个LSTM cell组成&#xff0c;是包含时间步的一个网络 2. LSTM cell 图2是LSTM在时间步上的结构&#xf…

「51媒体」教育论坛会议媒体邀约的资源有哪些

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 中国拥有众多教育方面的媒体资源&#xff0c;这些媒体在邀约时可以用于宣传和推广教育活动、论坛或项目。以下是一些具体的教育媒体邀约资源&#xff1a; 报纸类媒体&#xff1a; 《中…

STM32实现1.8寸液晶屏 LCD SPI串口显示屏模块 TFT彩屏(标准库和HAL库实现)

目录 一、所选模块 液晶模块选择&#xff08;淘宝上均有售卖&#xff09; 模块引脚 二、嵌入式单片机型号 三、接线表设计 四、开发环境版本说明 五、标准库实现 六、HAL库实现 七、完整工程&#xff08;内含标准库和HAL库源码&#xff09; 代码链接 一、所选模块 液…

OpenSSL实现AES的ECB和CBC加解密,可一次性加解密任意长度的明文字符串或字节流(QT C++环境)

本篇博文讲述如何在Qt C的环境中使用OpenSSL实现AES-ECB/CBC-Pkcs7加/解密&#xff0c;可以一次性加解密一个任意长度的明文字符串或者字节流&#xff0c;但不适合分段读取加解密的&#xff08;例如&#xff0c;一个4GB的大型文件需要加解密&#xff0c;要分段读取&#xff0c;…

Android system property运作流程源码分析

一.序 前文分析了build.prop这个系统属性文件的生成&#xff0c;每个属性都有一个名称和值&#xff0c;他们都是字符串格式。属性被大量使用在Android系统中&#xff0c;用来记录系统设置或进程之间的信息交换。属性是在整个系统中全局可见的。每个进程可以get/set属性&#x…

【ITK配准】第十六期 2D中BSpline可变形多分辨率配准样例

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 公众号:VTK忠粉 前言 本文分享ITK配准中的2D中BSpline可变形多分辨率配准,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO 2…

文件摆渡系统与传统文件交换有什么区别|好用的文件摆渡系统分享

一、文件摆渡系统与传统文件交换方式的区别 文件摆渡系统与传统文件交换方式在多个方面存在显著的区别。随着信息化和网络化的发展&#xff0c;文件交换的方式也在不断演进&#xff0c;从传统的物理介质交换到现代化的网络交换&#xff0c;文件摆渡系统作为其中的一种重要方式…

PT:pt write_change to innovus 脚本

我正在「拾陆楼」和朋友们讨论有趣的话题&#xff0c;你⼀起来吧&#xff1f; 拾陆楼知识星球入口 #!usr/bin/perl open rf,"$ARGV[0]"; open wf,">test. tcl"; while (<rf>) { s/\[(\D[^\s\]]*)?\]/\\\[$1\\\]/g; if (/c…