【问题标题】:Python str subclass represents a value which is not a real stringPython str 子类表示一个不是真实字符串的值
【发布时间】:2020-09-03 17:11:30
【问题描述】:

我是python的新手。致力于扩展旧模块。到目前为止,它有一个返回 str 的函数(阻塞 shell 命令的输出)。现在我需要该函数也能够返回一个对象,以便以后可以对其进行操作(检查非阻塞 shell 命令的输出)。因此,该函数现在返回我从str 子类化的类的一个实例,以实现向后兼容性。然而,问题是,当这样一个对象被传递给 os.path.isdir - 它总是返回 False,即使字符串是有效路径

import os


class ShellWrap(str):

    def __new__(cls, dummy_str_value, process_handle):
        return str.__new__(cls,"")

    def __init__(self, dummy_str_value, process_handle):
        self._ph = process_handle
        self._output_str = ""

    def wait_for_output(self):
        # for simplicity just do 
        self._output_str = "/Users"

    def __str__(self):
        return str(self._output_str)

    def __repr__(self):
        return str(self._output_str)

    def __eq__(self,other):
        if (isinstance(other, str)):
            return other == str(self._output_str)
        else:
            return super().__eq__(self,other)

>>> obj = ShellWrap("",None)
>>> obj.wait_for_output()

>>> print(type(obj))
... <class '__main__.ShellWrap'>

>>> print (ShellWrap.__mro__)
... <class '__main__.ShellWrap'>
   (<class '__main__.ShellWrap'>, <class 'str'>, <class 'object'>)

>>> print(type(obj._output_str))
... <class 'str'>

>>> print(obj)
... /Users

>>> print(obj._output_str)
... /Users
>>> obj == "/Users"
... True

让我困惑的是:

 >>> print(os.path.isdir(obj))
... False **<<-- This one puzzles me**
print(os.path.isdir("/Users"))
... True

我尝试添加 PathLike 继承并再实现一个 dunder 但没有成功:

class ShellWrap(str,PathLike):
   ....
   def __fspath__(self):
      return self._output_str

似乎还有一个 dunder 我未能实现。但是哪个?

但是,我确实在调试器中看到了一些奇怪的东西。当我将手表放在obj 上时 - 它说它属于str 类,但调试器显示的值没有引号(与其他“纯”strs 不同)。 在调试器中手动为字符串添加引号 - 使其工作,但我猜编辑字符串可能会创建一个新对象,这次是纯 str。

我错过了什么?

编辑:在意识到(请参阅已接受的答案)我尝试做的事情是不可能的之后,我决定挑战必须继承 str 的决定。 所以现在我的班级没有继承任何东西。它只是实现了__str__, __repr__ and __fspath__,这似乎就足够了!显然,只要str 继承存在 - 它获得优先权,dunders 就不会被调用,它会欺骗一些库去获取 str 值的底层 C 存储

【问题讨论】:

  • 试试os.path.isdir(str(obj)),看看是否有效
  • @Luka,谢谢。我知道它有效,但我不能。我不允许在课堂外更改代码。我需要构建我的类,使其完全兼容 str...
  • 如果你选择 PathLike,在 __fspath__ 你可以 return str(self._output_str) ,不是吗?
  • &gt;&gt;&gt; print(type(obj._output_str)) ... &lt;class 'str'&gt;以来没有任何改变

标签: python string class inheritance subclass


【解决方案1】:

考虑os.path.isdir 的来源。当您传入obj 时,您可能会触发该值错误,因为您要评估的字符串是字符串子类的属性,而不是子类应该表示的字符串。您必须在 str 的源代码中多花点功夫才能找到要覆盖的正确成员。

编辑:解决此问题的一种可能方法是动态使用__init__。也就是说,获取在__new__ 中呈现路径字符串所需的一切,然后在该方法中返回类之前,将output_str 设置为属性。现在在您的__init__ 中,调用super().__init__,并以self.output_str 作为唯一参数。

【讨论】:

  • 在哪里可以找到内置函数的源代码?
  • 没有“要覆盖的正确成员”。 __init__ 也无济于事 - str__init__ 中没有任何作用。它甚至没有自己的__init__——它继承了object.__init__
  • __new__ 中准备好一切是可行的方法,但这需要等待 shell 命令的输出,并且问题希望对象创建是非阻塞的。
  • 好吧,也许不是__init__,但是有一个入口点,因为str 接受一个参数!
  • @LukaKostic 据我所知,这似乎不是一个重复的问题 :)
【解决方案2】:

你想做的事是不可能的。

处理字符串的 C 代码访问由str 类管理的实际字符串数据,而不是您正在编写的方法。它并不关心您将另一个字符串作为属性附加到您的对象,或者您覆盖了一堆方法。它比your_obj.__whatever__() 更接近str.__whatever__(your_obj),尽管它根本不经过方法调用。

在这种情况下,相关的 C 代码是 os.path.isdir 委托给的 os.stat 调用,但几乎任何使用字符串的东西都会使用用 C 编写的东西,在某些时候直接访问 str 数据。

您希望对象的数据是可变的 - wait_for_output 是可变的 - 但您不能改变从 str 继承的对象部分,这才是重要的数据。

【讨论】:

  • 你是什么意思 C 代码使用字符串 - 用 C 实现的 os.path.isdir 会改变这里的行为吗?我在哪里可以阅读更多关于它的信息以更好地理解。如果我需要担心某些功能是如何实现的以及它们是否尊重语言流,这听起来像是破坏语言的东西。但我可能会错过这里的大局
  • 如果他继承了一个路径对象并在路径对象函数中返回了一个普通的python字符串,那是什么问题?
  • @LukaKostic:你可以争辩说应该这样做,但事实并非如此,引入__fspath__ 的设计动机并不包括干扰现有的str 行为。
  • @LouisShraga:您实际上不需要继承os.PathLike 来实现__fspath__。它不像 Java 接口。无论如何,如果这样可行,那么您的 str 向后兼容性需求比您想象的要低很多。
  • 就个人而言,我会在这里分开角色。在调用wait_for_output 后,我不会让一个对象的值突然改变,而是让wait_for_output 存储并返回一个输出字符串。对于当前的设计,客户在wait_for_output 调用之前使用错误值的风险太大。
猜你喜欢
  • 2017-11-28
  • 1970-01-01
  • 1970-01-01
  • 2022-09-22
  • 2022-08-16
  • 1970-01-01
  • 2022-07-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多