2021年1月17日 星期日

[ Python 文章收集 ] How to Create Read-Only and Deletion Proof Attributes in your Python Classes

 Source From Here

Control your attributes
This tutorial will demonstrate how to simply create read-only and deletion proof attributes in your Python class. In this way, an extra layer of control can be applied to your attributes, ensuring that your class is being used as intended. While there are multiple ways to create read-only and deletion proof attributes in Python, use of the setattr and delattr dunder methods are a convenient way to quickly control the attributes in your objects.

Example: The Employee Class
To illustrate, lets envisage that we are creating a simple Employee class that requires three values to be set in the object. These values will be the employee’s name, their age, and their unique id number. These attribute values will be set in the attributes; '_name', _'age', and '_id_number' and denoted with an underscore to indicate that they are private attributes, not intended to be used outside the class.

Once the __init__ method has been set, it is necessary to define the magic method, __setattr__. In this method we can apply some simple logic to exert powerful attribute control. In the example presented, the __init__ method will call the __setattr__ method when the attributes are first set during object initialisation. We can prevent the _name attribute being reset, by applying the following logic.

To achieve, we can write a conditional that says, when a new attribute is being set, if the attribute is equal to _name and already has a value, then the program will exit and raise a built-in AttributeError exception with a custom message displayed to the standard error (stderr) output. The _name attribute is set during object initialization, so any reset to this attribute will raise the exception and terminate the program.

We could also exert some further control, by making sure that the _age and _id_number attributes are both of type integer. We can create a list and check that the _age and _id_number attributes are in that last before we set them as an integer. The code to achieve this is shown below in the setattr method.
  1. class Employees(object):  
  2.     def __init__(self, name, age, id_number):  
  3.         self._name = name  
  4.         self._age = age  
  5.         self._id_number = id_number  
  6.   
  7.     def __setattr__(self, key, value):  
  8.         if key == '_name' and hasattr(self, '_name'):  
  9.             raise AttributeError('The value for the name attribute has already been set, and can not be re-set')  
  10.         if key in ['_age''_id_number']:  
  11.             self.__dict__[key] = int(value)  
  12.         self.__dict__[key] = value  
  13.   
  14.     def __str__(self):  
  15.         return "Employees: " + ','.join(list(map(lambda t: f"{t[0]}={t[1]}", self.__dict__.items())))  
  16.   
  17.     def __repr__(self):  
  18.         return self.__str__()  
Note: In the examples shown, when an attribute is set, it is not set using the object.attribute syntax in the setattr magic method, but rather using object.__dict__[key] = valueIf we use the former approach, an endless recursive loop will be initiated, because the object.attribute syntax will call the setattr method!

Now, lets create an Employee object and check that the attributes have been set. As shown, the attributes have been set and are of the correct type:
>>> from test import *
>>> ep = Employees('John', 40, 123)
>>> ep
Employees: _name=John,_age=40,_id_number=123

Now, suppose, we would like to re-set the _name attribute, despite the fact it has already been set. In this case, the AttributeError exception is raised, and prints a message to the console informing the user that the name attribute has been set and cannot be re-set.
>>> ep._name = 'Ken'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\tmp\test.py", line 9, in __setattr__
raise AttributeError('The value for the name attribute has already been set, and can not be re-set')
AttributeError: The value for the name attribute has already been set, and can not be re-set

Deletion Proof Attributes
To make our name attribute deletion proof, we can simply add some logic to the under __delattr__ method. Here, we say that if the user attempts to delete the attribute name from the object, an AttributeError exception will be raised informing the user that the name attribute can not be deleted. However, we will still permit behaviour that enables users to delete any other attribute in the Employee object.
  1. class Employees(object):  
  2.     def __init__(self, name, age, id_number):  
  3.         self._name = name  
  4.         self._age = age  
  5.         self._id_number = id_number  
  6.   
  7.     def __setattr__(self, key, value):  
  8.         if key == '_name' and hasattr(self, '_name'):  
  9.             raise AttributeError('The value for the name attribute has already been set, and can not be re-set')  
  10.         if key in ['_age''_id_number']:  
  11.             self.__dict__[key] = int(value)  
  12.         self.__dict__[key] = value  
  13.   
  14.     def __delattr__(self, key):  
  15.         if key == '_name':  
  16.             raise AttributeError('The name attribute can not be deleted')  
  17.         else:  
  18.             del self.__dict__[key]  
  19.   
  20.     def __str__(self):  
  21.         return "Employees: " + ','.join(list(map(lambda t: f"{t[0]}={t[1]}", self.__dict__.items())))  
  22.   
  23.     def __repr__(self):  
  24.         return self.__str__()  
An AttributeError exception is now raised when we attempt to delete the protected name attribute from the Employeeep object.
>>> from test import *
>>> ep = Employees('John', 40, 123)
>>> del ep._name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\tmp\test.py", line 16, in __delattr__
raise AttributeError('The name attribute can not be deleted')
AttributeError: The name attribute can not be deleted


沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...