This is the 26th day of my participation in the August More Text Challenge

Python is an object-oriented language, and using object-oriented programming in general leads to more efficient development, better software quality, and more extensible, readable, and maintainable code. But on a larger project, if the entity classes are very numerous and have very complex attributes, you’ll start to feel like Python classes are really “tired” to write. }}}}}}}}}}}}}}}}}}}}}}}}}}}

class Box:
     def __init__(self, length, width, hight) :
         
         self.length = length
         self.width = width
         self.hight = hight
    
Copy the code

Self. XXX = XXX = XXX = XXX = XXX = XXX = XXX = XXX = XXX = XXX = XXX = XXX = XXX = XXX = XXX = XXX = XXX = XXX = XXX = XXX

And we know that in Python, when you want to customize the printouts of the object itself, you implement the __repr__() method in its class, for example:

    def __repr__(self) :
        return '{}(length={}, width={}, hight={})'.format(
            self.__class__.__name__, self.length, self.width, self.hight)
Copy the code

Implements the __repr__() method, which prints our custom characters only when we print the object itself.

box = Box(20.15.15)
print(box)
Width =15, width=15, hight=15
Copy the code

But sometimes we get stuck because we don’t want to implement the __repr__() method because it’s cumbersome, but it’s unfriendly not to print.

If we want to implement object comparison, sometimes we need to judge whether two objects are equal or compare the size, we need to implement __eq__(), __lt__(), __gt__() and other methods to achieve the comparison between objects, for example:

def __eq__(self, other) :
    if not isinstance(other, self.__class__):
        return NotImplemented
    return (self.length, self.width, self.hight) == (
        other.length, other.width, other.hight)
Copy the code

So we need to implement these methods for comparison. Let’s say we implemented all of the methods mentioned above and then suddenly added a property to get a property so that the entire class method needs to be modified, which is very grueling. Is there a way to automatically add something like this to a class? Yes, there is. The attrs module will help you define classes easily.

The use of attrs

We can install using PIP install attrs.

Then modify the above code:

from attr import attrs, attrib


@attrs
class Box:
    length = attrib(type=int, default=0)
    width = attrib(type=int, default=0)
    hight = attrib(type=int, default=0)


box1 = Box(20.15.15)
print(box1)
box2 = Box(30.20.20)
print(box2 == box1)
print(box2 > box1)
Copy the code

The Box class is decorated with attrs in the module, and all attributes are defined using attrib, specifying their types and default values. And we didn’t implement any of the methods mentioned above, but we did implement all of them. Now if we add a property, color, that does not participate in the object comparison but has to be printed out, and so on, we can add a property, which has to participate in the object comparison but has to be printed out, which is very simple:

from attr import attrs, attrib


@attrs
class Box:
    length = attrib(type=int, default=0)
    width = attrib(type=int, default=0)
    hight = attrib(type=int, default=0)
    color = attrib(repr=True, cmp=False, default=(0.0.0))
    hardness = attrib(repr=False, cmp=True, default=0)


box1 = Box(20.15.15, (255.255.255), 80)
print("box1:", box1)
box2 = Box(20.15.15, (255.255.0), 100)
print("box2:", box2)
print(box2 == box1)
print(box2 > box1)
Copy the code

The execution result is as follows:

In other words, if we use the Attrs library, the class definition will be efficient and concise, and there is no need to write redundant and complex code. For attrib(), accept the following arguments:

  • Default: The default value for the property, which is used if no initialization data is passed in
  • Validator: validator that checks whether an argument passed in is valid
  • Repr: Whether to participate in the output when an object is printed
  • CMP: Indicates whether to participate in object comparison
  • Hash: Indicates whether to deduplicate
  • Init: Whether to participate in the initialization. If False, this parameter cannot be used as a class initialization parameter. The default is True
  • Metadata: Read-only additional data
  • Type: the type, such as int, STR, etc., defaults to None
  • To do some value processing and converter to increase fault tolerance
  • Kw_only: whether it is a mandatory keyword parameter. The default value is False

We’ll just focus on validators and converters here; the rest of the parameters are easy to understand.

The validator

Sometimes certain conditions must be met when setting an attribute, such as the color attribute above. We use RGB three primary colors, such as black is (255,255,255). In this case, we need to verify whether the attribute is legal.

Such as:

def color_is_valid(instance, attr, value) :
    if not isinstance(value, set) :raise ValueError(F "parameter{attr.name}:{value}Illegal!")
    for i in value:
        if not 0 <= i<= 255:
            raise ValueError(F "parameter{attr.name}:{value}Illegal!")




@attrs
class Box:
    length = attrib(type=int, default=0)
    width = attrib(type=int, default=0)
    hight = attrib(type=int, default=0)
    color = attrib(repr=True, cmp=False, validator=color_is_valid, default=(0.0.0))
    hardness = attrib(repr=False, cmp=True, default=0)


box1 = Box(20.15.15, (255.255.260), 80)
Copy the code

The execution result is as follows:The above code defines a validator color_is_valid() method to verify that the color color is valid. If it is not, an exception will be thrown.

The validator method takes three arguments:

  • Instance: class object
  • Attr: indicates the attribute name
  • Value: indicates the value of an attribute

The attrs module also provides a number of built-in validators, which won’t be covered here.

converter

For example, if an attribute receives an int, we want to pass in a numeric string without an error. Then we can add a converter to convert the string to a number automatically, for example:

def to_int(value) :
    try:
        return int(value)
    except:
        return None


@attrs
class Box:
    length = attrib(type=int, default=0, converter=to_int)
    width = attrib(type=int, default=0, converter=to_int)
    hight = attrib(type=int, default=0)
    color = attrib(repr=True, cmp=False, default=(0.0.0))
    hardness = attrib(repr=False, cmp=True, default=0)


box1 = Box("20"."15".15, (255.255.255), 80)
print("box1:", box1)
box2 = Box("2a".15.15, (255.255.0), 100)
print("box2:", box2)
Copy the code

Above, we define a method to_int() that converts a value to a numeric type. The conversion exception returns None, which is very fault tolerant.

conclusion

The attrs module is very similar to the ORM framework. It allows you to do business without having to do initialization. The goal of attrs is to help developers write clean and efficient code without slowing down their programming. Classes Without Boilerplate

Finally, thank my girlfriend for her tolerance, understanding and support in work and life!