关于《Effective Python》Item31中Exam类变量设计的疑问
Great question—this is a common point of confusion for readers working through that example, so let’s unpack it clearly.
First, let’s clarify: the author’s choice to make math_grade, writing_grade, and science_grade class variables (instead of instance variables) is a deliberate teaching decision, not a design mistake. Here’s why:
The core goal of this example is to demonstrate descriptors’ ability to track global state across multiple instances. By using a class-level descriptor with a global
_grade_dict, the author is showing how descriptors can hold and manage state that’s shared across all instances of the host class (Exam, in this case). This is a lesser-known but powerful feature of descriptors, and the example prioritizes highlighting this capability over building a "production-perfect" Exam class.You’re absolutely right that in a real-world scenario, making these instance variables would be more logical. If we initialized each grade as an instance variable (e.g., in
Exam.__init__), each student’s grades would be stored directly on their instance, and the Grade descriptor wouldn’t need a global dictionary to map instances to values. This design would be more encapsulated and aligned with how we’d model student grades in practice.
For context, here’s what that more practical implementation might look like (avoiding the global dictionary):
class Grade: def __init__(self, attribute_name): self.attribute_name = attribute_name def __get__(self, instance, owner): # Return the value stored directly on the instance return instance.__dict__.get(self.attribute_name, 0) def __set__(self, instance, value): if not (0 <= value <= 100): raise ValueError("Grade must be between 0 and 100") # Store the value directly on the instance's __dict__ instance.__dict__[self.attribute_name] = value class Exam: math_grade = Grade("math_grade") writing_grade = Grade("writing_grade") science_grade = Grade("science_grade")
In this version, each Exam instance holds its own grade values, and the Grade descriptor only handles validation—no global state required.
So to wrap up: the original example’s design isn’t a mistake. It’s a simplified teaching tool focused on illustrating a specific descriptor feature (global state tracking) rather than optimizing the Exam class for real use. Your intuition about the more practical instance-variable approach is totally correct for production code.
内容的提问来源于stack exchange,提问作者Baron Yugovich

