Understanding Python Properties and Descriptors
This article explains Python's property decorator and descriptor protocol, showing how to turn methods into managed attributes, perform validation, compute derived values, and illustrates their implementation with clear code examples including a Student class, a custom descriptor, and a classmethod reimplementation.
Python offers powerful attribute management through the property decorator, which lets you define getter, setter, and deleter methods for a class attribute, enabling validation and custom behavior when accessing or modifying the attribute.
For example, a Student class can use @property , @name.setter , and @name.deleter to control the name attribute, ensuring it is a string and allowing controlled deletion.
class Student:
def __init__(self, name, birth, math, art):
self._math = math
self.__birth = birth
self._name = name
self._art = art
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not isinstance(value, str):
raise ValueError("name类型不对")
self._name = value
@name.deleter
def name(self):
del self._name
stu1 = Student('liming', 1900, 12, 34)
print(stu1.name)
stu1.name = 123 # ValueError: name类型不对
del stu1.nameSimilar validation can be applied to other attributes such as art , ensuring the value is an integer within 0‑100, and you can define computed properties like age that calculate a value from stored data.
Properties improve code safety by allowing type checks and other logic during attribute access, which is especially useful in Python where assignments can otherwise change types silently.
Descriptors are a lower‑level mechanism that implement the methods __get__ , __set__ , and __delete__ . By defining a class that provides any of these methods, you can control how attribute access behaves.
class StrDescriptor:
def __init__(self):
self._value = 0
def __get__(self, instance, owner):
return str(self._value)
def __set__(self, instance, value):
if 0 <= len(str(value)) <= 20:
self._value = value
def __delete__(self, instance):
if instance.allow_del:
del self._value
else:
print("不可以删除属性")
class Email:
username = StrDescriptor()
address = StrDescriptor()
allow_del = False
mail = Email()
mail.username = 12345
print(mail.username) # "12345"
del mail.username # 删除失败,提示不可以删除属性The descriptor example shows how __get__ returns a string representation, __set__ enforces length constraints, and __delete__ can conditionally allow deletion based on a flag.
Even built‑in features like classmethod are descriptors; a custom reimplementation demonstrates that classmethod stores the original function and defines __get__ to return a wrapper that passes the class as the first argument.
class classmethod(object):
def __init__(self, f):
print(f)
self.func = f
def __get__(self, instance, owner=None):
print("in classmethod __get__")
def newfunc(*args):
print(self)
print(owner)
print(instance)
return self.func(owner, *args)
return newfunc
class Test:
a = 1
@classmethod
def myfunc(cls, value):
cls.a = value
print("hello")
Test.myfunc(123) # hello
print(Test.a) # 123Overall, properties and descriptors provide a flexible way to encapsulate attribute logic, enforce validation, compute derived values, and implement advanced behaviors such as class methods, making Python code more robust and expressive.
Byte Quality Assurance Team
World-leading audio and video quality assurance team, safeguarding the AV experience of hundreds of millions of users.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.