- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
Python之禅说:
“应该有一种——最好只有一种——显而易见的方法来做到这一点。”
假设我想创建一个构建金融交易的类。该类应允许用户构建交易,然后调用 sign()
方法签署交易,为通过 API 调用广播做准备。
该类将具有以下参数:
sender
recipient
amount
signer (private key for signing)
metadata
signed_data
所有这些都是字符串,除了 amount 是一个 int,除了最后两个之外都是必需的:metadata 是一个可选参数,signed_data 在方法 sign()
被调用。
我们希望所有参数在签名发生之前都经过某种验证,这样我们就可以通过向用户提出适当的错误来拒绝格式错误的交易。
使用经典的 Python 类和构造函数,这似乎很简单:
class Transaction:
def __init__(self, sender, recipient, amount, signer, metadata=None):
self.sender = sender
self.recipient = recipient
self.amount = amount
self.signer = signer
if metadata:
self.metadata = metadata
def is_valid(self):
# check that all required parameters are valid and exist and return True,
# otherwise return false
def sign(self):
if self.is_valid():
# sign transaction
self.signed_data = "pretend signature"
else:
# raise InvalidTransactionError
或具有属性:
class Transaction:
def __init__(self, sender, recipient, amount, signer, metadata=None):
self._sender = sender
self._recipient = recipient
self._amount = amount
self._signer = signer
self._signed_data = None
if metadata:
self._metadata = metadata
@property
def sender(self):
return self._sender
@sender.setter
def sender(self, sender):
# validate value, raise InvalidParamError if invalid
self._sender = sender
@property
def recipient(self):
return self._recipient
@recipient.setter
def recipient(self, recipient):
# validate value, raise InvalidParamError if invalid
self._recipient = recipient
@property
def amount(self):
return self._amount
@amount.setter
def amount(self, amount):
# validate value, raise InvalidParamError if invalid
self._amount = amount
@property
def signer(self):
return self._signer
@signer.setter
def signer(self, signer):
# validate value, raise InvalidParamError if invalid
self._signer = signer
@property
def metadata(self):
return self._metadata
@metadata.setter
def metadata(self, metadata):
# validate value, raise InvalidParamError if invalid
self._metadata = metadata
@property
def signed_data(self):
return self._signed_data
@signed_data.setter
def signed_data(self, signed_data):
# validate value, raise InvalidParamError if invalid
self._signed_data = signed_data
def is_valid(self):
return (self.sender and self.recipient and self.amount and self.signer)
def sign(self):
if self.is_valid():
# sign transaction
self.signed_data = "pretend signature"
else:
# raise InvalidTransactionError
print("Invalid Transaction!")
我们现在可以在设置时验证每个值,所以在我们签名时我们知道我们有有效的参数,is_valid()
方法只需要检查是否已设置所有必需的参数.与在单个 is_valid()
方法中进行所有验证相比,这对我来说感觉更像 Pythonic,但我不确定所有额外的样板代码是否真的值得。
使用数据类:
@dataclass
class Transaction:
sender: str
recipient: str
amount: int
signer: str
metadata: str = None
signed_data: str = None
def is_valid(self):
# check that all parameters are valid and exist and return True,
# otherwise return false
def sign(self):
if self.is_valid():
# sign transaction
self.signed_data = "pretend signature"
else:
# raise InvalidTransactionError
print("Invalid Transaction!")
将此与方法进行比较
1,这还不错。它简洁、干净、可读,并且已经内置了 __init__()
、__repr__()
和 __eq__()
方法。另一方面,与Approach相比
2 我们回到通过大量的 is_valid()
方法验证所有输入。
我们可以尝试将属性与数据类一起使用,但这实际上比听起来更难。根据this blog post可以这样做:
@dataclass
class Transaction:
sender: str
_sender: field(init=False, repr=False)
recipient: str
_recipient: field(init=False, repr=False)
. . .
# properties for all parameters
def is_valid(self):
# if all parameters exist, return True,
# otherwise return false
def sign(self):
if self.is_valid():
# sign transaction
self.signed_data = "pretend signature"
else:
# raise InvalidTransactionError
print("Invalid Transaction!")
有没有一种而且只有一种明显的方法可以做到这一点?是否建议将数据类用于此类应用程序?
最佳答案
作为一般规则,不限于 Python,编写“fails fast”代码是个好主意:也就是说,如果在运行时出现问题,您希望它被检测到并发出信号(例如,通过尽早抛出异常。
特别是在调试上下文中,如果错误是设置了无效值,您希望在设置值时抛出异常,以便堆栈跟踪包括设置无效值的方法。如果在使用该值时抛出异常,则您无法指示代码的哪一部分导致了无效值。
在您的三个示例中,只有第二个示例允许您遵循这一原则。它可能需要更多的样板代码,但与没有有意义的堆栈跟踪的调试相比,编写样板代码很容易并且不会花费太多时间。
顺便说一句,如果您有执行验证的 setter ,那么您也应该从构造函数中调用这些 setter ,否则可能会创建一个初始状态无效的对象。
关于Pythonic 类和 Python 之禅,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58702396/
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题? Update the question所以它是on-topic对于堆栈溢出。 10年前关闭。 Improve this qu
我是一名优秀的程序员,十分优秀!