Почему признаки, лишенные класса меняются apesar распределения их в другую переменную?

Определяется класс с 'частным' так называемым признаком __rango потом хочет быть сделанной копия этого признака в переменной rangoT чтобы это изменять, не касаясь первоначального признака, но два меняют Вашу стоимость.

Определение класса:

class Nivel():

    def __init__(self):
        self.__rango = []

    def setRango(self, rango=[]):
        self.__rango = rango

    def getRango(self):
        return self.__rango

    def jugarCopiaRango(self):
        rangoT = []
        rangoT = self.getRango()
        rangoT[0] = 100
        print('la copia del rango luego de la modificación')
        print(rangoT)

Давайте верить в инстанцию класса и мы используем методы:

if __name__ == "__main__":
    n = Nivel()
    n.setRango([12, 11, 14, 15])
    print('el rango original')
    print(n.getRango())
    n.jugarCopiaRango()
    print('el rango original no debería cambiar pero:')
    print(n.getRango())

Результат:

первоначальный ранг
[12, 11, 14, 15]
копия ранга после изменения
[100, 11, 14, 15]
первоначальный ранг не был бы должен меняться, но:
[100, 11, 14, 15]

Что должно делаться, чтобы не изменять в частный признак __rango когда он изменяется в rangoT. Используется Пайтон 3.6.4

2
задан 11.04.2018, 00:41
2 ответа

Мы пойдем по частям, сначала признаки или mГ©todos лишенные строго они не существуют в Пайтоне , как я думаю, что уже ты знаешь ввиду того, что entrecomillas это "лишенный". Использование двойной порции underscore в начало идентификатора для этого конца - ошибка / непонимание histГіrico, что распространился создавая confusiГіn, пальто, потому что программисты склоняются к тому, чтобы пробовать использовать этот механизм, чтобы определять признаки, лишенные подобно языкам как C ++ или Java, что ошибка, потому что jamГЎs pensГі для этого конца.

С этим механизмом знает как ямс mangling . Когда любой идентификатор предпочитает подчеркнутую двойную порцию (без двойной порции, подчеркнутой в конце концов) он заменяется automГЎticamente _nombreDeLaClase__identificador.

Идея состоит в том, чтобы предотвращать конфликты между идентификаторами классов и например предотвращать возможные нежелательные sobreescrituras случайной формы в наследстве:

class A:
    def __bar(self):  # Método "privado"
        print("Soy el método bar de la clase A")

    def bar(self):    # Método "público"
        self.__bar()

class B(A):
    def __bar(self):
        print("Soy el método bar de la clase B")

>>> b = B()
>>> b.bar()
>>> Soy el método bar de la clase A

мы Можем видеть __bar B не sobreescribe __bar Вашего класса отец, потому что mГ©todos его перешли к тому, чтобы звать _A__bar и _B__bar соответственно. действительно, что это может давать ее sensaciГіn, которого говорится об одном mГ©todo "лишенный" потому что:

>>> b.__bar()
Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    b.__bar()
AttributeError: 'B' object has no attribute '__bar'

, Но я повторяю, они мы atributos/mГ©todos частные (понимая как таковой, что они не являются доступными никакой формы снаружи класса), это не Ваше использование и не должны быть использованными для этого. Его нет один mГ©todo лишенный, simplemte __bar не Ваше имя, любой может соглашаться в посредством:

>>> b._B__bar()
Soy el método bar de la clase B

Пайтон, хотя это шокирует и многие это возненавидели поэтому, он не имеет, как filosofГ, - чтобы помещать ограничения в программисте, полагают, что уже он "больше возраста" и осознает то, что он делает. Действительно есть случаи, в которых один mГ©todo/atributo не должен быть измененным снаружи Вашего класса, для этих случаев она convenciГіn он состоит в том, чтобы показывать, что переменная или mГ©todo он лишен посредством единственного underscore (без ущерба что explicite в ней documentaciГіn). Это продолжать быть идентификатором равно как другим, одинокая отметка, чтобы показывать разработчикам, что они должны относиться к этому как лишенный, и что доступ в прямо может подразумевать риск.

Из-за Гєltimo, нужно иметь в виду, что процесс mangling осуществляется в течение нее definiciГіn класса , а именно, если мы делаем:

class Foo:
    def __init__(self):
        self.__bar = 4

inst = Foo()
inst.__bar = 7 

В inst.__bar = 7 переводчик ищет признак __bar между признаками инстанции и класса, как он это не находит благодаря тому, что признак, созданный с self.__bar назван в _Foo.__bar, переходят как с любой asignaciГіn, создает новый признак инстанции названные __bar и ему распределяется ссылка на 7. Но нужно помнить, что в этом случае не применяется ямс mangling для этого признака, так как он создается позже в нее definiciГіn класса. Нам может казаться это более ясным показывая признаки инстанции:

>>> inst.__dict__
{'_Foo__bar': 4, '__bar': 7}

Считая это ясным, мы не должны путать концепцию "лишенный" с концепцией "изменчивости". self.__rango - список и следовательно объект mutable , когда ты делаешь:

rangoT = self.getRango()

estГЎs распределяя ссылку (direcciГіn памяти) от self.__rango до rangoT не создавая копию той же самой , а именно оба идентификатора указывают на тот же объект в памяти. Как списки - mutables, когда ты пробуешь изменять объект посредством одной из переменных, не создают новый объект, просто изменяются. Указав на обе переменные в тот же объект, оба показывают изменение. Следующий вопрос считает базовой ту же ошибку:

Ошибка, изменив элементы в субсписках, все это заканчивают с тем же контентом

, Сказанные, концепция сеттера и getter в Пайтоне также они не существуют как таковые, в Вашем месте форма "pitГіnica" состоит в том, чтобы использовать properties , со всем этим ты cГіdigo podrГ, - чтобы быть:

class Nivel:
    def __init__(self):
        self._rango = []  # Atributo de instancia "privado"

    # "getter"
    @property
    def rango(self):
        return self._rango.copy()  # Retornamos una copia del atributo "privado"

    # "setter"
    @rango.setter
    def rango(self, rango=[]):
        self._rango = rango.copy()

    def jugar_copia_rango(self):
        rango_t = self.rango
        rango_t[0] = 100
        print('la copia del rango luego de la modificación')
        print(rango_t)


if __name__ == "__main__":
    n = Nivel()
    n.rango = [12, 11, 14, 15]
    print('El rango original:')
    print(n.rango)
    n.jugar_copia_rango()
    print('El rango original no cambia:')
    print(n.rango)

В этом случае, "getter" self. _ range возврати всегда копию того же самого. А именно, когда ты делаешь b.range или self.range, ты получаешь ссылку копии self._range и он не снабжает ссылками ее в объект в sГ - сам.

Замечает: то, что мы получаем, - то, чего знает как shallow copy , а именно, копируется объект (список), но элемент, который они содержат, они не скопированы и имелись между обоими списками. В этом случае ты хранишь целые числа, которые неизменные, но если твой признак self._range восприимчивый содержания элементов mutables, должен использовать "мертвую копию" для того, чтобы tambiГ©n Ваши элементы были скопированы. это делается посредством copy.deepcopy()

4
ответ дан 24.11.2019, 00:08
  • 1
    Спасибо за ответ ты послал меня исследовать pensé что python был как они говорят язык, который " он делает hincapié в синтаксисе, которому способствовал бы có я говорю legible". Для новых как я, кто хотят знать má s на propierties и декораторах возможно подайте им эту ссылку в inglé s programiz.com/python-programming/property –  Gabriel Asqui 11.04.2018, 02:21

Прежде всего, одна aclaraciГіn: в Пайтоне не существуют частные признаки, ни гораздо менее защищенные. Выражение это проблема это у тебя есть aquГ-:

def jugarCopiaRango(self):
    rangoT = []
    rangoT = self.getRango() # <---

Конечно estГЎs думая, что self.getRango() estГЎ возвращая "копию" первоначального списка self.__rango, когда в действительности то, что estГЎ, делая, отнесясь друг к другу о списке, состоит в том, чтобы возвращать ссылку на self.__rango sobreescribiendo начальная стоимость, что habГ-схвати данность. Из-за которого, сделав:

rangoT[0] = 100

в действительности estГЎs делая:

self.__rango[0] = 100

и имея в виду первое, что comentГ© относительно признаков, ясно estГЎs изменяя первоначальную стоимость. Если ты хочешь предотвратить это одна opciГіn serГ - в:

def getRango(self):
    return list(self.__rango)

, использовав list() estГЎs создавая "копию" переменной класса, в этом случае она actualizaciГіn последующий не impactarГЎ на первоначальном признаке.

2
ответ дан 24.11.2019, 00:08