Pythons str.join()的内部是什么? (隐藏输出密码)

时间:2016-11-08 10:07:33

标签: python

我偶然发现了一种有趣的(?)隐藏密码(和其他个人数据)从屏幕到日志文件的常规输出的方法。

在他的书How to make mistakes in Python中,Mike Pirnat建议为敏感字符串实现一个类,并重载其__str__ - 和__repr__ - 方法。

我试验了这个并得到了这个:

class secret(str):

    def __init__(self, s):
        self.string = s

    def __repr__(self):
        return "'" + "R"*len(self.string) + "'"

    def __str__(self):
        return "S" * len(self.string)

    def __add__(self, other):
        return str.__add__(self.__str__(), other)

    def __radd__(self, other):
        return str.__add__(other, self.__str__())

    def __getslice__(self, i, j):
        return ("X"*len(self.string))[i:j]

(我知道使用len提供了有关要隐藏的内容的信息。它只是用于测试。)

在这种情况下它可以正常工作:

pwd = secret("nothidden")

print("The passwort is " + pwd)                  # The passwort is SSSSSSSSS
print(pwd + " is the passwort.")                 # SSSSSSSSS is the password.

print("The passwort is {}.".format(pwd))         # The password is SSSSSSSSS.
print(["The", "passwort", "is", pwd])            # ['The', 'password', 'is', 'RRRRRRRRR']
print(pwd[:])                                    # XXXXXXXXX

然而,这不起作用:

print(" ".join(["The", "password", "is", pwd]))  # The password is nothidden

那么,str.join()如何在内部工作?我需要使用哪种方法来覆盖字符串?

1 个答案:

答案 0 :(得分:5)

问题在于您继承自str,它可能实现__new__,这意味着即使您避免在类中调用父构造函数,底层C对象仍然会使用它进行初始化。

现在join可能正在检查它是否有str子类,并且在C中实现,它直接访问底层C结构,或使用其他str相关函数绕过__str____repr__(想一想:如果值是字符串或字符串子类,为什么代码会调用__str____repr__来获取其值?只是以某种方式访问​​底层字符数组!)

要解决此问题:继承str!不幸的是,这意味着在某些情况下你将无法像字符串一样使用该对象,但这几乎是不可避免的。

可能有效的替代方法是实施__new__并为str的{​​{1}}方法提供不同的值:

__new__

结果是:

class secret(str):
    def __new__(cls, initializer):
        return super(secret, cls).__new__(cls, 'X'*len(initializer))
    def __init__(self, initializer):
        self.text = initializer
    def __repr__(self):
        return "'{}'".format("R"*len(self))
    def __str__(self):
        return "S"*len(self)
    def __add__(self, other):
        return str(self) + other
    def __radd__(self, other):
        return other + str(self)

然而,我没有真正看到它是如何有用的。我的意思是:这个类的目的是避免编程错误,最终显示敏感信息?但是,触发异常会更好,以便您可以识别错误!为此,In [19]: pwd = secret('nothidden') In [20]: print("The passwort is " + pwd) # The passwort is SSSSSSSSS ...: print(pwd + " is the passwort.") # SSSSSSSSS is the password. ...: ...: print("The passwort is {}.".format(pwd)) # The password is SSSSSSSSS. ...: print(["The", "passwort", "is", pwd]) # ['The', 'password', 'is', 'RRRRRRRRR'] ...: print(pwd[:]) The passwort is SSSSSSSSS SSSSSSSSS is the passwort. The passwort is SSSSSSSSS. ['The', 'passwort', 'is', 'RRRRRRRRR'] XXXXXXXXX In [21]: print(" ".join(["The", "password", "is", pwd])) The password is XXXXXXXXX raise NotImplementedError内的__str__可能是最好的,而不是默默地提供无用的价值......确保你没有泄漏秘密,但发现错误变得非常困难。