2016-11-08 3 views
11

私は、スクリーンからログファイルへの一般的な出力からパスワード(および他の個人データ)を隠す興味深い(?)方法を見つけました。Pythonのstr.join()の内部構造は何ですか? (出力からパスワードを隠す)

彼の本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()は内部的にどのように動作しますか?文字列を隠すためにどのメソッドをオーバーロードする必要がありますか?

答えて

5

strから継承している可能性があります。__new__が実装されています。これは、クラスで親コンストラクタを呼び出すことを避けたとしても、基礎となるCオブジェクトがまだ初期化されていることを意味します。

joinは、おそらくそれはstrサブクラスを持っていると、Cで実装されている場合は、そのアクセス直接基礎となるCの構造をチェックし、または(それについて考える__str____repr__をバイパスし、他のstr関連の機能を使用している次の場合?値が文字列または文字列のサブクラスである、コードの呼び出し__str__または__repr__はその値を得るために、なぜそれはちょうど、いくつかの方法で、基本となる文字列をアクセスする)

この問題を解決するには:!strからではない継承をを行います!残念ながら、これはいくつかの状況で文字列のようにオブジェクトを使用することはできませんが、それはかなり避けられないことです。

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 


働くことの代替は__new__を実装し、str__new__方法に異なる値を供給することですこれがどのように役立つか本当に分かります。つまり、このクラスの目的は、機密情報の表示につながるプログラミングエラーを避けることです。しかし、その後例外が発生した方が、バグを識別できるようになります!そのためには、raise NotImplementedErrorの中に__str____repr__を入れておいて、無意味な価値を提供するのではなく、おそらく秘密を漏らさないでください。

+1

この種の作品。 'print(" "、" password "、" is "、pwd)))'は、トレースバックを生成しますが、 'str()'に 'pwd'をラップすると、望ましい出力が生成されます。もちろん、元のバージョンも修正されます。私はこれが少し安全だと思う、隠れた文字列を印刷する代わりにトレースバックを生成する。 – TigerhawkT3

+0

@ TigerhawkT3私は期待どおりに機能する実装を提供しました。なぜこれが本当にひどく有用ではないのかについてのメモを追加しました。何かを表示したくない場合は、メソッドを呼び出すか、呼び出されたときに例外を送出します。 – Bakuriu

+0

プログラミング*エラー*についてはそれほど大したことではありませんが、RESTクエリなどをログに記録すると都合がよいので、毎回自分自身を隠す必要はありません。 そして、最も重要な部分は好奇心でした。 しかし、あなたは正しいかもしれません。賢明なデータの印刷を避けるより良い方法があるでしょう。 –

関連する問題