我知道在python中,内置的object()会返回一个前哨对象。我很好奇它是什么,但主要是它的应用。
发布于 2016-09-04 14:36:01
object是Python3中所有其他类继承而来的基类,对于一个普通的老对象,你不能做很多事情。然而,对象的标识可能是有用的。例如,iter函数接受一个sentinel参数,该参数表示何时停止终止。我们可以为它提供一个object()。
sentinel = object()
def step():
inp = input('enter something: ')
if inp == 'stop' or inp == 'exit' or inp == 'done':
return sentinel
return inp
for inp in iter(step, sentinel):
print('you entered', inp)这将要求输入,直到用户键入stop、exit或done。我不能确切地确定什么时候带有前哨的iter比生成器更有用,但我猜它无论如何都很有趣。
我不确定这是否回答了你的问题。需要说明的是,这只是object的一个可能的应用程序。从根本上说,它在python语言中的存在与它作为一个前哨数值(据我所知)的可用性无关。
发布于 2018-10-19 07:57:31
这是dataclasses的Python标准库中有关使用标记值的源代码示例
# A sentinel object to detect if a parameter is supplied or not. Use
# a class to give it a better repr.
class _MISSING_TYPE:
pass
MISSING = _MISSING_TYPE()发布于 2021-01-18 22:27:11
Python中的对象标识和类
语句“我知道在python中内置对象()返回一个标记对象”。有点离谱,但并不是完全错误,所以让我先说明一下,以确保我们的想法是一致的:
Python中的object()仅仅是所有类的父类。在Python2中,这是显式的。在Python 2中,你必须写:
class Foo(object):
...来获得一个所谓的“新风格对象”。你也可以在没有超类的情况下定义类,但这只是为了向后兼容,对这个问题并不重要。
现在在Python3中,object超类是隐式的。所以所有的类都继承自那个类。因此,以下两个类在Python 3中是相同的:
class Foo:
pass
class Foo(object):
pass了解了这一点,我们可以稍微修改一下您的初始声明:
...内置对象()返回一个标记对象。
然后变成:
...内置对象()返回" object“类的对象实例。
所以,在写的时候:
my_sentinel = object()简单地创建一个空的对象实例“在内存中的某处”。最后一部分很重要,因为在默认情况下,内置的id()函数和使用... is ...进行检查都依赖于内存地址。例如:
>>> a = object()
>>> b = object()
>>> a is b
False这为您提供了一种创建对象实例的方法,您可以使用该实例来检查代码中的某种逻辑,否则这些逻辑是非常困难甚至不可能的。这是“”对象的主要用途。
示例用例:区分“无”和“无/未初始化/空/...”
有时,值None是变量的有效值,您可能需要检测"empty“或与None类似的值之间的差异。
让我们假设您有一个类正在为一个开销很大的操作执行延迟加载,其中"None“是一个有效值。然后你可以这样写它:
#: sentinel value for uninitialised values
UNLOADED = object()
class MyLoader:
def __init__(self, remote_addr):
self.value = UNLOADED
self.remote_addr = remote_addr
def get(self):
if self.value is UNLOADED:
self.value = expensive_operation(self.remote_addr)
return self.value现在expensive_operation可以返回任何值。即使没有或任何其他“假”值和“缓存”也不会出现意外的错误。它还使代码具有很好的可读性,因为它非常清楚地向代码块的读者传达了意图。您还可以为一个额外的"is_loaded“布尔值节省存储空间(尽管可能是否定的)。
使用布尔值的相同代码:
class MyLoader:
def __init__(self, remote_addr):
self.value = None
self.remote_addr = remote_addr
self.is_loaded = False # <- need for an additional variable
def get(self):
if not self.is_loaded:
self.value = expensive_operation(self.remote_addr)
self.is_loaded = True # <- source for a bug if this is forgotten
return self.value或者,使用"None“作为默认值:
class MyLoader:
def __init__(self, remote_addr):
self.value = None # <- We'll use this to detect load state
self.remote_addr = remote_addr
def get(self):
if self.value is None:
self.value = expensive_operation(self.remote_addr)
# If the above returned "None" we will never "cache" the result
return self.value最后的想法
上面的"MyLoader“示例只是一个可以方便使用标记值的示例。它们有助于使代码更具可读性和表现力。它们还可以避免某些类型的bug。
在试图使用None来表示特殊值的区域中,它们特别有用。每当你想到“当X是这种情况时,我会将变量设置为None”时,考虑使用标记值可能是值得的。因为您现在为值None赋予了特定上下文的特殊含义。
另一个这样的例子是对无限整数有特殊的值。无穷大的概念只存在于浮点数中。但是如果你想确保类型安全,你可能想要创建你自己的“特殊”值来表示无穷大。
使用像这样的标记值有助于区分多个不同的概念,否则这是不可能的。如果您需要许多不同的“特殊”值,并且到处使用None,那么您可能最终会在一个概念的上下文中使用另一个概念中的None,并最终产生意想不到的副作用,这可能很难调试。想象一下这样一个人为设计的函数:
SENTINEL_A = object()
SENTINEL_B = object()
def foobar(a = SENTINEL_A, b = SENTINEL_B):
if a is SENTINEL_A:
a = -12
if b is SENTINEL_B:
b = a * 2
print(a+b)通过使用这样的标记,就不可能因为混淆变量而意外触发if分支。例如,假设您重构代码并在某处出错,混合a和b如下所示:
SENTINEL_A = object()
SENTINEL_B = object()
def foobar(a = SENTINEL_A, b = SENTINEL_B):
if b is SENTINEL_A: # <- bug: using *b* instead of *a*
a = -12
if b is SENTINEL_B:
b = a * 2
print(a+b)在这种情况下,第一个if永远不会为真(当然,除非该函数被错误地调用)。如果您使用None作为默认设置,则此错误将变得更难检测,因为在您不期望它的情况下,您将最终使用a = -12。
从这个意义上说,前哨使你的代码更健壮。如果你的代码中出现了逻辑错误,它们将更容易被发现。
话虽如此,前哨数值是相当罕见的。我个人发现它们对于避免在标记特殊情况时过度使用None非常有用。
https://stackoverflow.com/questions/39313943
复制相似问题