В Питоне переменные передаются по ссылке или по значению? Есть подводные камни?
Неизменяемые объекты передаются по значению. Это значит, что при изменении значения переменной будет создан новый объект. К этому типу относятся:
- числовые данные (int, float, complex)
- символьные строки (str)
- кортежи (tuple) При инициализации переменной незменяемого типа создается объект (например, целое число), этот объект имеет некоторый идентификатор:
>>> a = 10 >>> id(a) 10914656
оператор = связывает переменную a и объект посредством ссылки. При этом вы не можете изменить сам объект, т.е. когда вы присвоите переменной новое значение, интерпретатор создаст новый объект (если до этого этот объект был создан, то переменная просто получит ссылку), а первоначальный объект удалится из памяти сбощиком мусора, если ссылок на него больше нет.
Изменяемые (mutable)
Изменяемые объекты передаются по ссылке. Это значит, что при изменении значения переменной объект будет изменен. К этому типу относятся:
- списки (list)
- множества (set)
- словари (dict)
Подводные камни
Создадим список a , установим для переменной b ссылку на a , прибавим к b элемент списка и выведем их значения и идентификаторы на экран:
>>> a = [1, 2] >>> b = a >>> b.append(3) >>> print(a, b) [1, 2, 3] [1, 2, 3] >>> print(id(a), id(b)) 139748057891656 139748057891656
Как мы видим, переменные имеют одинаковые id и элементы списка. Если ты не знаешь об этой особенности изменяемых объетов, то такое поведение программы для тебя становится полной неожиданностью и может привести к ошибке в работе программы. Таким же образом с помощью ссылки на изменяемый объект, переменная передается в функцию:
>>> def add_value(a): . a.append(3) >>> b = [1, 2] >>> add_value(b) >>> print(b) [1, 2, 3]
Даже возвращая None , функция изменила список b , чего бы нам не хотелось.
Что с этим можно сделать
Для того, чтобы передать в функцию изменяемую переменную как значение, нужно сделать копию изменяемого элемента. Создадим копию списка:
новый_лист = старый_лист[:]
Тоже самое можем сделать вот так:
новый_лист = list(старый_лист)
Переменная новый_лист ссылается на новый объект:
>>> id(старый_лист), id(новый_лист) (139748050279112, 139748057891656)
Это дает нам возможность изменять оба объекта независимо друг от друга.
Что почитать
- Переменные-ссылки в Python
- Understanding Python variables and Memory Management
Попробуйте бесплатные уроки по Python
Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.
Переходите на страницу учебных модулей «Девмана» и выбирайте тему.
Примеры работы с классами в Python
Python — объектно-ориентированный язык с начала его существования. Поэтому, создание и использование классов и объектов в Python просто и легко. Эта статья поможет разобраться на примерах в области поддержки объектно-ориентированного программирования Python. Если у вас нет опыта работы с объектно-ориентированным программированием (OOП), ознакомьтесь с вводным курсом или учебным пособием, чтобы понять основные понятия.
Создание классов
Оператор class создает новое определение класса. Имя класса сразу следует за ключевым словом class , после которого ставиться двоеточие:
class ClassName: """Необязательная строка документации класса""" class_suite
- У класса есть строка документации, к которой можно получить доступ через ClassName.__doc__ .
- class_suite состоит из частей класса, атрибутов данных и функции.
Пример создания класса на Python:
class Employee: """Базовый класс для всех сотрудников""" emp_count = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.emp_count += 1 def display_count(self): print('Всего сотрудников: %d' % Employee.empCount) def display_employee(self): print('Имя: <>. Зарплата: <>'.format(self.name, self.salary))
- Переменная emp_count — переменная класса, значение которой разделяется между экземплярами этого класса. Получить доступ к этой переменной можно через Employee.emp_count из класса или за его пределами.
- Первый метод __init__() — специальный метод, который называют конструктором класса или методом инициализации. Его вызывает Python при создании нового экземпляра этого класса.
- Объявляйте другие методы класса, как обычные функции, за исключением того, что первый аргумент для каждого метода self . Python добавляет аргумент self в список для вас; и тогда вам не нужно включать его при вызове этих методов.
Создание экземпляров класса
Чтобы создать экземпляры классов, нужно вызвать класс с использованием его имени и передать аргументы, которые принимает метод __init__ .
# Это создаст первый объект класса Employee emp1 = Employee("Андрей", 2000) # Это создаст второй объект класса Employee emp2 = Employee("Мария", 5000)Доступ к атрибутам
Получите доступ к атрибутам класса, используя оператор . после объекта класса. Доступ к классу можно получить используя имя переменой класса:
emp1.display_employee() emp2.display_employee() print("Всего сотрудников: %d" % Employee.emp_count)Теперь, систематизируем все.
class Employee: """Базовый класс для всех сотрудников""" emp_count = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.emp_count += 1 def display_count(self): print('Всего сотрудников: %d' % Employee.emp_count) def display_employee(self): print('Имя: <>. Зарплата: <>'.format(self.name, self.salary)) # Это создаст первый объект класса Employee emp1 = Employee("Андрей", 2000) # Это создаст второй объект класса Employee emp2 = Employee("Мария", 5000) emp1.display_employee() emp2.display_employee() print("Всего сотрудников: %d" % Employee.emp_count)При выполнении этого кода, мы получаем следующий результат:
Имя: Андрей. Зарплата: 2000 Имя: Мария. Зарплата: 5000 Всего сотрудников: 2Вы можете добавлять, удалять или изменять атрибуты классов и объектов в любой момент.
emp1.age = 7 # Добавит атрибут 'age' emp1.age = 8 # Изменит атрибут 'age' del emp1.age # Удалит атрибут 'age'Вместо использования привычных операторов для доступа к атрибутам вы можете использовать эти функции:
- getattr(obj, name [, default]) — для доступа к атрибуту объекта.
- hasattr(obj, name) — проверить, есть ли в obj атрибут name .
- setattr(obj, name, value) — задать атрибут. Если атрибут не существует, он будет создан.
- delattr(obj, name) — удалить атрибут.
hasattr(emp1, 'age') # возвращает true если атрибут 'age' существует getattr(emp1, 'age') # возвращает значение атрибута 'age' setattr(emp1, 'age', 8) #устанавливает атрибут 'age' на 8 delattr(empl, 'age') # удаляет атрибут 'age'Встроенные атрибуты класса
Каждый класс Python хранит встроенные атрибуты, и предоставляет к ним доступ через оператор . , как и любой другой атрибут:
- __dict__ — словарь, содержащий пространство имен класса.
- __doc__ — строка документации класса. None если, документация отсутствует.
- __name__ — имя класса.
- __module__ — имя модуля, в котором определяется класс. Этот атрибут __main__ в интерактивном режиме.
- __bases__ — могут быть пустые tuple, содержащие базовые классы, в порядке их появления в списке базового класса.
Для вышеуказанного класса давайте попробуем получить доступ ко всем этим атрибутам:
class Employee: """Базовый класс для всех сотрудников""" emp_count = 0 def __init__(self, name, salary): self.name = name self.salary = salary Employee.empCount += 1 def display_count(self): print('Всего сотрудников: %d' % Employee.empCount) def display_employee(self): print('Имя: <>. Зарплата: <>'.format(self.name, self.salary)) print("Employee.__doc__:", Employee.__doc__) print("Employee.__name__:", Employee.__name__) print("Employee.__module__:", Employee.__module__) print("Employee.__bases__:", Employee.__bases__) print("Employee.__dict__:", Employee.__dict__)Когда этот код выполняется, он возвращает такой результат:
Employee.__doc__: Базовый класс для всех сотрудников Employee.__name__: Employee Employee.__module__: __main__ Employee.__bases__: (,) Employee.__dict__: , 'display_count': , 'display_employee': , '__dict__': , '__weakref__': >Удаление объектов (сбор мусора)
Python автоматически удаляет ненужные объекты (встроенные типы или экземпляры классов), чтобы освободить пространство памяти. С помощью процесса ‘Garbage Collection’ Python периодически восстанавливает блоки памяти, которые больше не используются.
Сборщик мусора Python запускается во время выполнения программы и тогда, когда количество ссылок на объект достигает нуля. С изменением количества обращений к нему, меняется количество ссылок.
Когда объект присваивают новой переменной или добавляют в контейнер (список, кортеж, словарь), количество ссылок объекта увеличивается. Количество ссылок на объект уменьшается, когда он удаляется с помощью del , или его ссылка выходит за пределы видимости. Когда количество ссылок достигает нуля, Python автоматически собирает его.
a = 40 # создали объект b = a # увеличивает количество ссылок c = [b] # увеличивает количество ссылок del a # уменьшает количество ссылок b = 100 # уменьшает количество ссылок c[0] = -1 # уменьшает количество ссылок
Обычно вы не заметите, когда сборщик мусора уничтожает экземпляр и очищает свое пространство. Но классом можно реализовать специальный метод __del__() , называемый деструктором. Он вызывается, перед уничтожением экземпляра. Этот метод может использоваться для очистки любых ресурсов памяти.
Пример работы __del__()
Деструктор __del__() выводит имя класса того экземпляра, который должен быть уничтожен:class Point: def __init__(self, x=0, y=0): self.x = x self.y = y def __del__(self): class_name = self.__class__.__name__ print('<> уничтожен'.format(class_name)) pt1 = Point() pt2 = pt1 pt3 = pt1 print(id(pt1), id(pt2), id(pt3)) # выведите id объектов del pt1 del pt2 del pt3Когда вышеуказанный код выполняется и выводит следующее:
17692784 17692784 17692784 Point уничтоженВ идеале вы должны создавать свои классы в отдельном модуле. Затем импортировать их в основной модуль программы с помощью import SomeClass .
Наследование класса в python
Наследование — это процесс, когда один класс наследует атрибуты и методы другого. Класс, чьи свойства и методы наследуются, называют Родителем или Суперклассом. А класс, свойства которого наследуются — класс-потомок или Подкласс.
Вместо того, чтобы начинать с нуля, вы можете создать класс, на основе уже существующего. Укажите родительский класс в круглых скобках после имени нового класса.
Класс наследник наследует атрибуты своего родительского класса. Вы можете использовать эти атрибуты так, как будто они определены в классе наследнике. Он может переопределять элементы данных и методы родителя.
Синтаксис наследования класса
Классы наследники объявляются так, как и родительские классы. Только, список наследуемых классов, указан после имени класса.
class SubClassName(ParentClass1[, ParentClass2, . ]): """Необязательная строка документации класса""" class_suiteПример наследования класса в Python
class Parent: # объявляем родительский класс parent_attr = 100 def __init__(self): print('Вызов родительского конструктора') def parent_method(self): print('Вызов родительского метода') def set_attr(self, attr): Parent.parent_attr = attr def get_attr(self): print('Атрибут родителя: <>'.format(Parent.parent_attr)) class Child(Parent): # объявляем класс наследник def __init__(self): print('Вызов конструктора класса наследника') def child_method(self): print('Вызов метода класса наследника') c = Child() # экземпляр класса Child c.child_method() # вызов метода child_method c.parent_method() # вызов родительского метода parent_method c.set_attr(200) # еще раз вызов родительского метода c.get_attr() # снова вызов родительского методаКогда этот код выполняется, он выводит следующий результат:
Вызов конструктора класса наследника Вызов метода класса наследника Вызов родительского метода Атрибут родителя: 200Аналогичным образом вы можете управлять классом с помощью нескольких родительских классов:
class A: # объявите класс A . class B: # объявите класс B . class C(A, B): # C наследуется от A и B .Вы можете использовать функции issubclass() или isinstance() для проверки отношений двух классов и экземпляров.
- Логическая функция issubclass(sub, sup) возвращает значение True , если данный подкласс sub действительно является подклассом sup .
- Логическая функция isinstance(obj, Class) возвращает True , если obj является экземпляром класса Class или является экземпляром подкласса класса.
Переопределение методов
Вы всегда можете переопределить методы родительского класса. В вашем подклассе могут понадобиться специальные функции. Это одна из причин переопределения родительских методов.
Пример переопределения методов:
class Parent: # объявите родительский класс def my_method(self): print('Вызов родительского метода') class Child(Parent): # объявите класс наследник def my_method(self): print('Вызов метода наследника') c = Child() # экземпляр класса Child c.my_method() # метод переопределен классом наследникомКогда этот код выполняется, он производит следующий результат:
Вызов метода наследникаПопулярные базовые методы
В данной таблице перечислены некоторые общие функции. Вы можете переопределить их в своих собственных классах.
| № | Метод, описание и пример вызова |
|---|---|
| 1 | __init__(self [, args. ]) — конструктор (с любыми необязательными аргументами) obj = className(args) |
| 2 | __del__(self) — деструктор, удаляет объект del obj |
| 3 | __repr__(self) — программное представление объекта repr(obj) |
| 4 | __str__(self) — строковое представление объекта str(obj) |
Пример использования __add__
Предположим, вы создали класс Vector для представления двумерных векторов. Что происходит, когда вы используете дополнительный оператор для их добавления? Скорее всего, Python будет против.
Однако вы можете определить метод __add__ в своем классе для добавления векторов и оператор + будет вести себя так как нужно.
class Vector: def __init__(self, a, b): self.a = a self.b = b def __str__(self): return 'Vector (<>, <>)'.format(self.a, self.b) def __add__(self, other): return Vector(self.a + other.a, self.b + other.b) v1 = Vector(2, 10) v2 = Vector(5, -2) print(v1 + v2)При выполнении этого кода, мы получим:
Vector(7, 8)Приватные методы и атрибуты
Атрибуты класса могут быть не видимыми вне определения класса. Вам нужно указать атрибуты с __ вначале, и эти атрибуты не будут вызваны вне класса.
Пример приватного атрибута:
class JustCounter: __secret_count = 0 def count(self): self.__secret_count += 1 print(self.__secret_count) counter = JustCounter() counter.count() counter.count() print(counter.__secret_count)При выполнении данного кода, имеем следующий результат:
1 2 Traceback (most recent call last): File "test.py", line 12, in print(counter.__secret_count) AttributeError: 'JustCounter' object has no attribute '__secret_count'Вы можете получить доступ к таким атрибутам, так object._className__attrName . Если вы замените свою последнюю строку следующим образом, то она будет работать.
Как передать функцию как параметр в Python?
Поскольку функция в Python является объектом, ее можно передавать в другую функцию в качестве аргумента. При этом функция в аргументе указывается без круглых скобок, то есть передается только ссылка на функцию.
def calculate(a, b, operation): result = operation(a, b) return result def sum(a, b): return a + b def multiply(a, b) return a * b calculate(2, 3, sum) # 5 calculate(3, 4, multiply) # 12Функции, которые могут принимать другие функции в качестве аргументов, также называются функциями высшего порядка. Яркий пример функции высшего порядка - функция map() , которая принимает аргументом функцию и итерируемую последовательность, и применяет функцию-параметр к каждому элементу последовательности.
[python]Экземпляр класса как параметр по умолчанию
А че нет, замыкания же. Только param инициализируется один раз, это надо помнить.
creepnee
( 28.09.10 00:11:22 MSD )
мочь то можно, но это рассадник багов.
Zubchick ★
( 28.09.10 00:14:12 MSD )
Ответ на: комментарий от Zubchick 28.09.10 00:14:12 MSDпросто мне лень один объект все время явно передавать в разные функции, вот подумал, может его как параметр по умолчанию указать, но как это сделать не знаю
swelf ★
( 28.09.10 00:24:04 MSD ) автор топика
Глобальная переменная, нэ? Ее хотя бы поменять можно.
baverman★★★
( 28.09.10 04:36:43 MSD )а не судьба вот так сделать:
[code] def f(param = null): if !a a = A() a.bla = 'bla' [/code]
питона не знаю, но, думаю, суть ясна
anonymous
( 28.09.10 08:01:51 MSD )
Ответ на: комментарий от swelf 28.09.10 00:24:04 MSD
true_admin ★★★★★
( 28.09.10 11:20:07 MSD )
Ответ на: комментарий от anonymous 28.09.10 08:01:51 MSD
анонимус прав на все 100. Так и делают когда хочется дефолтовым параметром передать, например пустой список. Но с объектом сложней, они могут иметь отличные от А() параметры. Вообще изменять объекты внутри функций не являющихся методами черевато, но никто не говорит, что этого нельзя делать.
Zubchick ★
( 28.09.10 11:29:42 MSD )
Ответ на: комментарий от true_admin 28.09.10 11:20:07 MSD«Глобальный синглтон» в python - это модуль. Только и всего.
shylent ★
( 28.09.10 12:12:18 MSD )
Ответ на: комментарий от shylent 28.09.10 12:12:18 MSD
не обязательно. И там есть свои нюансы.
true_admin ★★★★★
( 28.09.10 13:02:34 MSD )
Ответ на: комментарий от true_admin 28.09.10 13:02:34 MSDЭх, ну вы же понимаете, что про все что угодно можно сказать, что там «есть свои нюансы» без всяких объяснений.
Просто, в самом деле, наипростейший вариант реализации синглтона без всяких вывертов (тем более, что все эти выверты без проблем в python обходятся, благодаря его динамизму), это объект внутри модуля.
shylent ★
( 28.09.10 14:03:27 MSD )
Ответ на: комментарий от shylent 28.09.10 14:03:27 MSD
В моём понимании класс тоже может быть синглтоном и я бы загнал все глобальные переменные в класс(для красоты). Про нюанс я имел в виду что если сделать «from module import *», где * это простые типы данных типа int, str итп то работать не будет т.к. они при присвоении значения получится своя локальная копия переменной которая и будет изменена.
true_admin ★★★★★
( 28.09.10 14:32:50 MSD )
Ответ на: комментарий от true_admin 28.09.10 14:32:50 MSDпро синглтоны почитаю потом какнить, пока решил просто сделать функцию методом класса.


