Python 使用property 重构类

重构的实例

重构前的设计问题

  • Phone 类中同时存在 get_device_id() 方法和 device_id 属性,造成了接口的不一致性
  • 初始化逻辑分散,设备 ID 的获取可能在多个地方发生
  • PackageManager 需要了解 Phone 类的内部实现细节
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Phone:
def __init__(self):
self.__device_id = None

def set_device_id(self, device_id):
self.__device_id = device_id

@property
def device_id(self):
return self.__device_id

@device_id.setter
def device_id(self, value):
if isinstance(value, str):
self.__device_id = value
else:
raise TypeError("device_id must be a string")
def get_device_id(self):
device_list = []
if self.__device_id is None:
# 执行adb指令获取手机序列号
outputs = os.popen("adb devices").read().split("\n")
for output in outputs:
if "\t" in output:
device_list.append(output.split('\t')[0])
if len(device_list) == 0:
print("未检测到手机!")
return None
else:
self.__device_id = device_list[0]
print("手机序列号:{}".format(device_list[0]))
return device_list[0]
else:
return self.__device_id

class PackageManager:
def __init__(self, device: Phone):
self.device = device
self.phone_device_id = self.device.device_id if self.device.device_id else self.device.get_device_id()

重构后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Phone:
def __init__(self):
self.__device_id = None

@property
def device_id(self):
"""
懒加载方式获取设备ID
如果ID不存在,则自动获取;如果存在,则直接返回
"""
if self.__device_id is None:
self.__device_id = self.__detect_device()
return self.__device_id

@device_id.setter
def device_id(self, value: str):
if not isinstance(value, str):
raise TypeError("device_id must be a string")
self.__device_id = value

def __detect_device(self) -> str | None:
"""私有方法,用于检测设备"""
device_list = []
outputs = os.popen("adb devices").read().split("\n")
for output in outputs:
if "\t" in output:
device_list.append(output.split('\t')[0])

if not device_list:
print("未检测到手机!")
return None

print(f"手机序列号: {device_list[0]}")
return device_list[0]

class PackageManager:
def __init__(self, device: Phone):
self.device = device
if self.device.device_id is None:
raise ValueError("No device detected")

改进点:

a) 使用懒加载模式

  • 只在真正需要 device_id 时才去获取
  • 避免了重复的检查和获取逻辑

b) 简化接口

  • 移除了 get_device_id() 方法,统一使用 property
  • 将设备检测逻辑移到私有方法中,隐藏内部实现细节

c) 更清晰的职责划分

  • Phone 类完全负责设备 ID 的管理和获取
  • PackageManager 只需要关心设备是否可用

好的重构通常遵循以下原则:

  1. 保持行为不变 - 外部接口和功能保持一致
  2. 提高可读性 - 代码更容易理解
  3. 提高可维护性 - 更容易修改和扩展
  4. 减少重复 - 消除重复代码
  5. 单一职责 - 每个类和方法只做一件事

重构小技巧

Python 在重构时有许多独特且强大的优势:

  1. 动态特性优势

    • 鸭子类型(Duck Typing):允许更灵活的代码重构
    • 动态属性和方法:可以轻松替换和修改类的行为
  2. 反射和自省能力

    1
    2
    3
    4
    # 动态检查和修改对象属性
    hasattr(obj, 'method') # 检查方法是否存在
    getattr(obj, 'method', default_value) # 安全获取属性
    setattr(obj, 'new_method', new_function) # 动态添加方法
  3. 装饰器

    在不修改原函数的情况下添加功能

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def logger(func):
    def wrapper(*args, **kwargs):
    print(f"Calling {func.__name__}")
    return func(*args, **kwargs)
    return wrapper

    @logger
    def some_function():
    pass # 无需修改原函数即可添加日志
  4. 上下文管理器

    简化资源管理和异常处理

    1
    2
    3
    4
    5
    6
    7
    8
    class RefactoredResource:
    def __enter__(self):
    # 资源初始化
    return self

    def __exit__(self, exc_type, exc_val, exc_tb):
    # 资源清理
    pass
  5. 类型提示(Type Hints)

    提供代码重构时的静态类型检查

    1
    2
    3
    4
    5
    6
    7
    from typing import List, Optional, Union

    def refactored_function(
    items: List[str],
    optional_param: Optional[int] = None
    ) -> Union[str, None]:
    pass
  6. 函数式编程特性

    1
    2
    3
    # 使用 lambda 和高阶函数重构
    numbers = [1, 2, 3, 4]
    squared = list(map(lambda x: x**2, numbers))
  7. Property 装饰器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class RefactoredClass:
    def __init__(self):
    self._value = None

    @property
    def value(self):
    # 可以添加计算逻辑
    return self._value

    @value.setter
    def value(self, new_value):
    # 可以添加验证逻辑
    self._value = new_value
  8. 元编程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def add_method(cls):
    def new_method(self):
    print("Dynamically added method")
    setattr(cls, 'dynamic_method', new_method)
    return cls

    @add_method
    class MyClass:
    pass
  9. 数据类和 Dataclasses

    1
    2
    3
    4
    5
    6
    from dataclasses import dataclass, field

    @dataclass
    class RefactoredData:
    name: str
    value: int = field(default=0)
  10. 魔法方法(Magic Methods)

    1
    2
    3
    4
    5
    6
    7
    8
    class SmartCompare:
    def __eq__(self, other):
    # 自定义相等比较
    pass

    def __lt__(self, other):
    # 自定义小于比较
    pass

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 重构前
class OldClass:
def __init__(self, data):
self.data = data

def process(self):
result = []
for item in self.data:
if item > 0:
result.append(item * 2)
return result

# 重构后
from typing import List
from functools import reduce

class RefactoredClass:
def __init__(self, data: List[int]):
self._data = data

@property
def processed_data(self) -> List[int]:
return list(filter(lambda x: x > 0,
map(lambda x: x * 2, self._data)))

def reduce_data(self):
return reduce(lambda x, y: x + y, self.processed_data, 0)