Sometimesreadonly属性

2024-09-30 02:24:08 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个类CameraInterface,有两个属性:

  • recording(布尔值)
  • recording_quality(具有三个可能值的枚举)

用户可以按硬件按钮(这是一个Raspberry Pi项目)来选择recording_quality,但是如果相机已经recording,则硬件将闪烁/buzz/否则表明您在录制时无法更改recording_quality。你知道吗

我希望CameraInterface对象强制执行这个要求。我不希望未来的我能够忘记这个要求,并且不小心尝试设置recording_quality而不首先确保recording is False。做这件事最像Python的方法是什么?你知道吗

我最熟悉Objective-C,在这里我可以尝试在外部readonly创建属性,然后执行以下操作:

- (void)setQuality:(Quality)quality
    successHandler:(void (^)(void))successHandler
    failureHandler:(void (^)(void))failureHandler
{
    if ( self.recording ) {
        if ( failureHandler ) {
            failureHandler();
        }
    }
    else {
        self.quality = quality; // internally writable
        if ( successHandler ) {
            successHandler();
        }
    }
}

更新

关于我要找的东西的澄清:

  1. 如果可能的话,我希望避免对控制流使用异常。你知道吗
  2. 我想将recording_quality的有时可设置的特性构建到CameraInterface的编程接口中,这样您就不必等到运行时才发现自己做错了什么。你知道吗
  3. 我已经知道Python属性了。我只是在寻找使用它们的最佳方法(或替代方法)。你知道吗

更新2

我被Martijn Pieters’s answer说服使用运行时异常,并在尝试设置recording_quality时使用try/catch。让我信服的部分原因是我试图用Python实现我的Objective-C示例。这样做,我意识到它确实更适合静态类型的语言。让我信服的部分原因是Martijn答案中链接的答案,它更详细地解释了异常是Python控制流的一个非常正常的部分。你知道吗

这是上面Objective-C示例的Python版本。我把它放在这里作为对其他人的警告:

import types

class CameraInterface(object):
    def __init__(self):
        self._recording_quality = 1
        self.recording = False

    @property
    def recording_quality(self):
        return self._recording_quality

    def set_recording_quality(self, quality, success_handler, failure_handler):
        if type(success_handler) is not types.FunctionType:
            raise ValueError("You must pass a valid success handler")
        if type(failure_handler) is not types.FunctionType:
            raise ValueError("You must pass a valid failure handler")

        if self.recording is True:
            # reject the setting, and call the failure handler
            failure_handler()
        else:
            self._recording_quality = quality
            success_handler()

def success():
    print "successfully set"

def failure():
    print "it was recording, so we couldn't set the quality"

camera = CameraInterface()
print "recording quality starting at {0}".format(camera.recording_quality)

camera.set_recording_quality(3, success, failure)
camera.recording = True
camera.set_recording_quality(2, success, failure)

这张照片:

recording quality starting at 1
successfully set
it was recording, so we couldn't set the quality

Tags: selfiffailureisdefcamerahandlersuccess
3条回答

您可以使用properties

class CameraInterface(object):
    def __init__(self):
        self.recording = False
        self._quality = None

    @property
    def recording_quality(self):
        return self._quality

    @recording_quality.setter
    def recording_quality(self, new):
        if self.recording:
            print("Can't change quality while recording")
        else:
            self.quality = new

可以使用其setter引发异常的^{}

class CameraInterface(object):
    @property
    def recording_quality(self):
        return self._quality

    @recording_quality.setter
    def recording_quality(self, quality):
        if self.recording:
            raise CurrentlyRecordingError()
        self._quality = quality

其中CurrentlyRecordingError是一个自定义异常;它可以是^{}的子类。你知道吗

在Python中,使用exceptions是处理在特定情况下无法设置的属性的最自然的方法。另见Is it a good practice to use try-except-else in Python?

另一种方法是使用Look-before-you-leap方法,每次要设置质量值时,都必须显式地测试相机录制状态。这会使API复杂化,并且很容易导致竞速条件,即在测试状态之后但在更改质量设置之前,摄像机开始录制。你知道吗

对于一个异常,您可以使用Ask for Forgiveness;这简化了对象的使用,因为您假设可以进行质量设置,并且可以在异常处理程序中处理只读情况。你知道吗

从Python的角度(除了不使用属性之外)对Objective-C版本的进一步说明:

  • 当您可以使用^{}时,不要使用type(..) is [not] typeobj。你知道吗
  • 与测试types.FunctionType不同,您可以使用^{} function测试是否可以调用某些东西;函数不是唯一的可调用函数。你知道吗
  • 在Python中,您信任API的用户;如果他们传入了无法调用的内容,这是他们自己的错,您的对象不需要在这里进行显式类型检查。Python是一种成年人同意的语言。你知道吗
  • if语句已经在测试真值;使用is True不仅是多余的,而且在与其他比较运算符一起使用时会导致难以调试的错误,因为这些运算符是链接的。value == someothervalue is True并不是你所认为的意思。你知道吗

您需要为属性创建同名的Python方法,然后为getter和setter功能使用@propertydecorator:

class CameraInterface(object):
    def __init__(self):
        self.recording = False
        self._recording_quality = False

    def start_recording(self):
        # do stuff
        self.recording = True

    @property
    def recording_quality(self):
        return self._recording_quality

    @recording_quality.setter
    def recording_quality(self, value):
        if self.recording:
            raise Exception("Whoa! You're already recording!")
        self._recording_quality = value

这允许您执行以下操作:

camera = CameraInterface()
camera.recording_quality = HD
camera.start_recording()
camera.recording_quality = SD # Exception!

相关问题 更多 >

    热门问题