名称以两个下划线开头的实例属性被奇怪地重命名

问题描述

根据我的类的当前实现,当我尝试使用类方法获取私有属性的值时,我得到 function hide1() { document.querySelector("textarea.one").style.display = "block"; document.querySelector("textarea.two").style.display = "none"; document.querySelector("textarea.three").style.display = "none"; document.querySelector("button.menucopy1").style.display = "block"; document.querySelector("button.menucopy2").style.display = "none"; document.querySelector("button.menucopy3").style.display = "none"; } function hide2() { document.querySelector("textarea.one").style.display = "none"; document.querySelector("textarea.two").style.display = "block"; document.querySelector("textarea.three").style.display = "none"; document.querySelector("button.menucopy1").style.display = "none"; document.querySelector("button.menucopy2").style.display = "block"; document.querySelector("button.menucopy3").style.display = "none"; } function copytextfunction2() { var value = document.getElementById("myInput2").value; var copyText = document.createElement("textarea"); copyText.value = value; copyText.style.position = "fixed"; copyText.style.top = "-1000vh"; document.body.append(copyText); copyText.select(); copyText.setSelectionRange(0,99999); document.execCommand("copy"); console.log(value); copyText.remove(); } function copytextfunction3() { var value = document.getElementById("myInput3").value; var copyText = document.createElement("textarea"); copyText.value = value; copyText.style.position = "fixed"; copyText.style.top = "-1000vh"; document.body.append(copyText); copyText.select(); copyText.setSelectionRange(0,99999); document.execCommand("copy"); console.log(value); copyText.remove(); } function copytextfunction1() { var value = document.getElementById("myInput1").value; var copyText = document.createElement("textarea"); copyText.value = value; copyText.style.position = "fixed"; copyText.style.top = "-1000vh"; document.body.append(copyText); copyText.select(); copyText.setSelectionRange(0,99999); document.execCommand("copy"); console.log(value); copyText.remove(); } function hide3() { document.querySelector("textarea.one").style.display = "none"; document.querySelector("textarea.two").style.display = "none"; document.querySelector("textarea.three").style.display = "block"; document.querySelector("button.menucopy1").style.display = "none"; document.querySelector("button.menucopy2").style.display = "none"; document.querySelector("button.menucopy3").style.display = "block"; } 作为输出。关于我哪里出错的任何想法?

代码

<textarea class="one" id="myInput1" name="myInput1" readonly>
One
One
One</textarea>
</div>

<div class="textniz">
  <textarea class="two" id="myInput2" name="myInput2" readonly>
Two
Two
Two</textarea>
</div>

<div class="textniz">
  <textarea class="three" id="myInput3" name="myInput3" readonly>
Three
Three
Three</textarea>
</div>


<div class="navbar">
  <div class="container">
    <a class="logo" href=".//templates/chat.html">test</a>
  </div>
</div>

<div>
  <button class="menu">menu</button>

  <button class="image">image</button>

  <button onclick="hide1()" class="en">hide1</button>
  <button onclick="hide2()" class="gr">hide2</button>
  <button onclick="hide3()" class="ru">hide3</button>

  <button onclick="copytextfunction1()" class="menucopy1">copy1</button>
  <button onclick="copytextfunction2()" class="menucopy2">copy2</button>
  <button onclick="copytextfunction3()" class="menucopy3">copy3</button>
</div>

解决方法

这段代码发生了什么?

上面的代码看起来不错,但有些行为可能看起来不寻常。如果我们在交互式控制台中输入:

c = Catalog()
# vars() returns the instance dict of an object,# showing us the value of all its attributes at this point in time.
vars(c)

那么结果是这样的:

{'_Catalog__product_names': {}}

这很奇怪!在我们的类定义中,我们没有给任何属性命名为 _Catalog__product_names。我们将一个属性命名为 __product_names,但该属性似乎已重命名。

发生了什么

这种行为不是错误——它实际上是 Python 的一个特性,称为 private name mangling。对于您在类定义中定义的所有属性,如果属性名称以两个前导下划线开头——并且以两个尾随下划线结尾——那么该属性将像这样重命名。 __foo 类中名为 Bar 的属性将重命名为 _Bar__foo__spam 类中名为 Breakfast 的属性将重命名为 _Breakfast__spam;等等等等

名称修改仅在您尝试从类外部访问属性时发生。类中的方法仍然可以使用您在 __init__ 中定义的“私有”名称访问该属性。

你为什么想要这个?

我个人从未发现过此功能的用例,对此我有些怀疑。它的主要用例适用于您希望一个方法或一个属性可以在一个类中私有访问的情况,但不能通过相同的名称访问该类之外的函数或从该类继承的其他类.

(注意 YouTube 演讲是 2013 年的,演讲中的例子是用 python 2 编写的,所以例子中的一些语法与现代 python 有点不同——print 仍然是一个语句而不是比函数等)

以下是使用类继承时私有名称修改工作原理的说明:

>>> class Foo:
...   def __init__(self):
...     self.__private_attribute = 'No one shall ever know'
...   def baz_foo(self):
...     print(self.__private_attribute)
...     
>>> class Bar(Foo):
...   def baz_bar(self):
...     print(self.__private_attribute)
...     
>>> 
>>> b = Bar()
>>> b.baz_foo()
No one shall ever know
>>> 
>>> b.baz_bar()
Traceback (most recent call last):
  File "<string>",line 1,in <module>
  File "<string>",line 3,in baz_bar
AttributeError: 'Bar' object has no attribute '_Bar__private_attribute'
>>>
>>> vars(b)
{'_Foo__private_attribute': 'No one shall ever know'}
>>>
>>> b._Foo__private_attribute
'No one shall ever know'

基类 Foo 中定义的方法能够使用 Foo 中定义的私有名称访问私有属性。然而,在子类 Bar 中定义的方法只能通过使用其损坏的名称来访问私有属性;其他任何事情都会导致异常。

collections.OrderedDict 是标准库中一个类的 good example,它广泛使用了名称修改来确保 OrderedDict 的子类不能通过同名。

我该如何解决这个问题?

这里显而易见的解决方案是重命名您的属性,使其只有一个前导下划线,就像这样。这仍然向外部用户发出一个明确的信号,即这是一个私有属性,不应由类外的函数或类直接修改,但不会导致任何奇怪的名称修改行为:

from abc import ABC,abstractmethod

class Search(ABC):
    @abstractmethod
    def search_products_by_name(self,name):
        print('found',name)


class Catalog(Search):
    def __init__(self):
        self._product_names = {}
    
    def search_products_by_name(self,name):
        super().search_products_by_name(name)
        return self._product_names.get(name)


x = Catalog()
x._product_names = {'x': 1,'y':2}
print(x.search_products_by_name('x'))

另一种解决方案是使用名称 mangling 滚动,如下所示:

from abc import ABC,name)


class Catalog(Search):
    def __init__(self):
        self.__product_names = {}
    
    def search_products_by_name(self,name):
        super().search_products_by_name(name)
        return self.__product_names.get(name)


x = Catalog()
# we have to use the mangled name when accessing it from outside the class
x._Catalog__product_names = {'x': 1,'y':2}
print(x.search_products_by_name('x'))

或者——这可能更好,因为从类外部使用其被破坏的名称访问属性有点奇怪——就像这样:

from abc import ABC,name):
        super().search_products_by_name(name)
        return self.__product_names.get(name)

    def set_product_names(self,product_names):
        # we can still use the private name from within the class
        self.__product_names = product_names


x = Catalog()
x.set_product_names({'x': 1,'y':2})
print(x.search_products_by_name('x'))
,

双下划线的目的是避免与子类定义的名称发生名称冲突。这不是表明某物是“私有的”的一种方式,因为 Python 没有阻止访问的概念。

__ 有用的情况是:

class Product:
   discount = 5
   __init__(self,name,price):
      self.name = name
      self.price = price

class Item(Product):
   def discount(self):
      self.price = self.price * 0.9

discount 类和子类的方法名。如果使用 __discount,该变量的名称将变为 _Product__discount

如果没有子类,就没有必要使用__

在您的代码中没有理由使用 ABC,并且可以轻松地以更 Pythonic 的方式编写:

class Catalog:
    def __init__(self,products=None):
        self.products = products

    def search_products(self,name):
        return [item for item in self.products if name in item]


x = Catalog()
x.products = [{"x": 1},{"y": 2},{"x": 5}]
results = x.search_products(name="x")  
# [{'x': 1},{'x': 5}]

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...