杰瑞科技汇

Python 2.7 property如何实现属性访问控制?

Of course. The property built-in in Python 2.7 is a powerful and elegant way to create managed attributes. It allows you to replace a simple attribute access (like obj.x) with a method call (like obj.get_x()) without changing the public interface of your class. This is often called "data hiding" or "encapsulation."

Python 2.7 property如何实现属性访问控制?-图1
(图片来源网络,侵删)

Let's break down how it works in Python 2.7.

The Core Problem: Why Do We Need property?

Imagine you have a class that represents a temperature in Celsius.

# BEFORE: The "naive" way
class Celsius:
    def __init__(self, temperature):
        self.temperature = temperature
    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32
# This works fine...
t = Celsius(25)
print t.temperature  # Output: 25
print t.to_fahrenheit() # Output: 77.0
# But what if you want to add validation?
# You can't just do this, because it would break existing code:
# if temperature < -273.15:
#     raise ValueError("Temperature cannot be below absolute zero!")

You have two bad choices:

  1. Add validation in __init__: This only works for initial creation. What if someone changes the attribute later? t.temperature = -300 would be allowed.
  2. Use getter/setter methods: You change the public API. Instead of t.temperature, you force users to call t.get_temperature() and t.set_temperature(30). This is cumbersome and breaks existing code that used the attribute directly.

property solves this perfectly. It lets you keep the simple obj.attribute syntax while running custom code (like validation) behind the scenes.

Python 2.7 property如何实现属性访问控制?-图2
(图片来源网络,侵删)

How property Works in Python 2.7

The property() function can be called in two main ways:

The Simple Way: property(fget, fset, fdel, doc)

You pass it four optional arguments:

  • fget: A function to get the attribute (the "getter").
  • fset: A function to set the attribute (the "setter").
  • fdel: A function to delete the attribute (the "deleter").
  • doc: A docstring for the property.

Let's refactor our Celsius class using this method.

class Celsius:
    def __init__(self, temperature):
        # We call the setter here to ensure validation on initialization
        self.temperature = temperature
    def get_temperature(self):
        """This is the getter."""
        print("Getting value...")
        return self._temperature
    def set_temperature(self, value):
        """This is the setter."""
        print("Setting value...")
        if value < -273.15:
            raise ValueError("Temperature cannot be below absolute zero!")
        self._temperature = value
    def del_temperature(self):
        """This is the deleter."""
        print("Deleting value...")
        del self._temperature
    # This is the magic!
    # We create a property object named 'temperature'
    temperature = property(get_temperature, set_temperature, del_temperature,
                           "A temperature in Celsius.")
# --- Usage ---
# 1. Setting a value (calls the setter)
t = Celsius(37.5)
# Output: Setting value...
# 2. Getting a value (calls the getter)
print t.temperature
# Output:
# Getting value...
# 37.5
# 3. Deleting the attribute (calls the deleter)
del t.temperature
# Output: Deleting value...
# 4. Accessing the docstring
print Celsius.temperature.__doc__
# Output: A temperature in Celsius.
# 5. Validation is enforced
try:
    t = Celsius(-300)
except ValueError as e:
    print e
# Output: Setting value!
#         Temperature cannot be below absolute zero!

Key Points:

Python 2.7 property如何实现属性访问控制?-图3
(图片来源网络,侵删)
  • We use a name like _temperature (a single leading underscore) for the "backing" attribute. This is a Python convention for "internal use" variables.
  • When you write t.temperature = 25, Python calls the set_temperature method.
  • When you write print t.temperature, Python calls the get_temperature method.
  • The public interface remains clean: t.temperature, not t.get_temperature().

The "Decorator" Way (More Pythonic and Common)

This syntax was introduced in Python 2.4 and is much cleaner. It uses the @property and @{attribute_name}.setter decorators.

The logic is:

  1. @property: Decorates the getter method. This method defines the property and must return the value.
  2. @{property_name}.setter: Decorates the setter method. This method takes the value being assigned as an argument.

Let's rewrite the Celsius class using decorators.

class Celsius:
    def __init__(self, temperature):
        # The setter is called during __init__
        self.temperature = temperature
    @property
    def temperature(self):
        """This is the getter. It's a property now."""
        print("Getting value...")
        return self._temperature
    @temperature.setter
    def temperature(self, value):
        """This is the setter. It's linked to the 'temperature' property."""
        print("Setting value...")
        if value < -273.15:
            raise ValueError("Temperature cannot be below absolute zero!")
        self._temperature = value
    # You can also add a deleter if you want
    @temperature.deleter
    def temperature(self):
        """This is the deleter."""
        print("Deleting value...")
        del self._temperature
# --- Usage is EXACTLY the same as before ---
print("--- Using Decorator Syntax ---")
t = Celsius(40)
# Output: Setting value...
print t.temperature
# Output:
# Getting value...
# 40
t.temperature = 42
# Output: Setting value!
print t.temperature
# Output:
# Getting value...
# 42
del t.temperature
# Output: Deleting value!

This is the preferred method in modern Python (and it works the same in Python 3). It's more readable because the getter, setter, and deleter are all defined with the same name, clearly grouping them together.


Common Use Cases for property

  1. Validation: As shown above, ensuring a value is within a certain range.
  2. Lazy Computation: Calculating an attribute's value only the first time it's requested and then caching it.
  3. Logging/Debugging: Logging every time an attribute is accessed, modified, or deleted.
  4. Interface Consistency: Exposing a complex internal calculation as a simple attribute. For example, a Person class could have a full_name property that returns self.first_name + " " + self.last_name.

Example: Lazy Computation

class Circle:
    def __init__(self, radius):
        self.radius = radius
        self._area = None  # Cached value
    @property
    def area(self):
        """Calculates area on first access, then caches it."""
        if self._area is None:
            print("Calculating area...")
            self._area = 3.14159 * (self.radius ** 2)
        return self._area
c = Circle(10)
print c.area
# Output:
# Calculating area...
# 314.159
print c.area
# Output:
# 314.159  # No calculation this time, just returns the cached value

Summary: property in Python 2.7

Feature Description
Purpose To create managed attributes that look like simple data attributes but have custom logic (get, set, delete).
Syntax Two main ways: the property(fget, fset, fdel) function and the @property decorator syntax.
Decorator Syntax (Preferred) @property for the getter, @{name}.setter for the setter. This is cleaner and more common.
Use Cases Validation, lazy loading, logging, and providing a clean interface for complex logic.
Python 2 vs 3 The functionality is identical. The decorator syntax works in both. The only difference is that in Python 3, you can also use @{name}.deleter.
分享:
扫描分享到社交APP
上一篇
下一篇