Как стать автором
Обновить

Комментарии 19

Бы дополнить одной строкой про порядок подхватывания переменных (локальных и глобальных) при совпадающих именах.
Такое бывает даже по независящим причинам (сторонние библиотеки).

Приведённый пример с константой, кажется сильно натянутым:


SUDDEN_LIST = [0, 0, 0]

def print_list(some_list):
    for element in SUDDEN_LIST:
        print(element) 

print_list([1, 2, 3]) 

Разве вы напишете такое в реальном коде? Функция с названием print_list, принимающая аргумент some_list и за каким-то макаром печатающая глобальную переменную?

Эти примеры используются для объяснения работы областей, не более того.

В основном пишу на PHP, сейчас возникла неоходимость набросать небольшой скриптик на Python и столкнулся с непониманием. В Python, внутри класса нет собственной области видимости?


file_name = 'something'

class Updater:
    file_name = ''

    def __init__(self, file_name: str):
        self.file_name = file_name

updater = Updater(file_name)

В этом примере переменная file_name внутри __init__ может быть взята сразу из глобального скоупа (без прокидования через аргументы)?
PyCharm подсказывает мне: "Shadows name file_name from outer scope" (подсказка для аргумента в __init__). Это так непривычно для меня, т.е. я должен выдумывать другие имена аргументам, чтобы они не пересекались с перменными из глобального скоупа?

Правильней не выдумывать другие имена аргументам, а вообще избегать такого дизайна.

Можно, пожалуйста, конкретней?
Представим другой пример: есть два класса (ClassA, ClassB). Для работы классу А необходимо наличие инстанса класса B. В PHP хорошей практикой является использование агрегации:


class ClassA {
    public $b;

    public function __construct(ClassB $b)
    {
        $this->b = $b;
    }
}

class ClassB {
    public $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }
}

$b = new ClassB('test_name');
$a = new ClassA($b);

Т.е. инициализация класса B происходит в клиентском коде, после чего через аргументы передается в консруктор класса А, где сохраняем инстанс в локальную переменную класса, все дальнейшее взаимодейтсвии происходит внутри локальной области видимости.
Такой подход не применим в Python?

Т.е. инициализация класса B происходит в клиентском коде, после чего через аргументы передается в консруктор класса А, где сохраняем инстанс в локальную переменную класса, все дальнейшее взаимодейтсвии происходит внутри локальной области видимости.
Такой подход не применим в Python?

Конечно применим. Именно так и происходит. Более того, если класс B является вспомогательным классом A, и более нигде не нужен, его можно определить прямо внутри класса.
В вашем примере намешано все подряд.
file_name = 'something' # это глобальная переменная

class Updater:
file_name = '' # это переменная класса

def __init__(self, file_name: str):
self.file_name = file_name # это переменная экземпляра класса

Переменная класса это совершенно не тоже самое, что переменная экземпляра класса. Она одинакова для всех экземпляров класса. Ее можно сравнить со статической переменной.

Когда я иногда пишу на php, то тоже делаю вермишелекод. Но если вы решили использовать классы — глобальные переменные лучше вообще не использовать, да и переменные класса редко когда нужны.
А если вы хотите писать вермишель с глобальными переменными, тогда вам классы не нужны вовсе.
Переменная класса это совершенно не тоже самое, что переменная экземпляра класса. Она одинакова для всех экземпляров класса. Ее можно сравнить со статической переменной.

При всем уважении, позволю себе не согласиться. Это не переменная класса, это свойство класса, которое может изменяться в ходе работы с классом. В моем, случае file_name = '' — это инициализация свойства (которое, можно и опустить вообще), дальше в __init__ я присваиваю значение этому свойству, полученное через аргумент конструктора.


Когда я иногда пишу на php, то тоже делаю вермишелекод. Но если вы решили использовать классы — глобальные переменные лучше вообще не использовать, да и переменные класса редко когда нужны.
А если вы хотите писать вермишель с глобальными переменными, тогда вам классы не нужны вовсе.

В моем случае у меня всего-то две плоскости:
1) Глобальная область видимости, она же уровень клиентского кода.
2) Классы, у которых должны быть собственные локальные области видимости.


Например, в статье, как лайфхак предлагается обернуть клиентский код в функцию main и вызывать потом ее, вместо хранения клиентского кода в глобальном скоупе. В принципе, это решает мой изначальный вопрос, но выглядит сдегка костыльно что ли...

может изменяться в ходе работы с классом. В моем, случае file_name = '' — это инициализация свойства (которое, можно и опустить вообще), дальше в __init__ я присваиваю значение этому свойству, полученное через аргумент конструктора.

нет, вы не правы тут. К ней вы можете получить доступ через cls, а не self, в конструкторе вы ей ничего не присваиваете.
Вы может задумывали 2 плоскости, но в своем примере привели 3 плоскости.
вот попробуйте следующий пример:
class TestClass:
name = 'cls_name'
def __init__(self, s):
self.name = s

@classmethod
def get_cls_name(cls):
return cls.name

obj = TestClass('self_name')
print(obj.name)
print(TestClass.name)
print(obj.get_cls_name())

А потом заново переосмыслите области видимости классов в python. Можно называть их свойствами, можно переменными, суть от этого не изменится, то что в области класса и то что в области экземпляра класса — это разные вещи и не пересекаются никак.

Ухты ж емае, вы правы. Оказывается, в Python нет такого понятия как инициализация свойств.
Вот хорошая статья для таких же как и я (может кому-нибудь пригодится) https://www.toptal.com/python/python-class-attributes-an-overly-thorough-guide

Речь о том, что глобальные переменные это вообще зло, и вы в своем примере это наглядно показали.

Да, я понял, спасибо.
Как я упоминул ранее, в основном я пишу на PHP, а там глобальная область видимости работает иначе — например, внутри класса уже начинается другой скоуп и нельзя просто так взять и использовать в аргументах методов переменные из глобальной области видимости.


В итоге, я вынес свой клиентский код из глобального скоупа в функцию main, которую потом вызываю вот так:


if __name__ == '__main__':
    main()

Ну а определение всех свойст класса вынес в __init__, а не так, как в моем примере выше.

file_name — это переменная класса (обьекта-класса), в __ init __ вы имеете дело с уже с экземпляром, поэтому для доступа к переменной именно «класса» нужно написать
self.file_name = self.__class__.file_name
или
self.file_name = type(self).file_name

Просто имя file_name в аспекте начального значения работает по обычному для питона алгоритму вверх local -> nonlocal (вышестоящий обьект) -> global (уровень модуля)
«Определение класса» нельзя считать «вышестоящим обьектом»
НЛО прилетело и опубликовало эту надпись здесь

Стесняюсь спросить. Не реклама ли опять?

Зарегистрируйтесь на Хабре , чтобы оставить комментарий