Назначение значений по умолчанию в вызове функции - это запах кода.
def a(b=[]):
pass
Это сигнатура функции, которая не подходит. Не только из-за проблем, описанных в других ответах. Я не буду вдаваться в это.
Эта функция направлена на то, чтобы сделать две вещи. Создайте новый список и выполните функциональность, скорее всего, в указанном списке.
Функции, которые делают две вещи, являются плохими функциями, поскольку мы учимся на чистых практиках кода.
Атака на эту проблему с полиморфизмом мы будем расширять список python или переносить его в класс, а затем выполнять свою функцию на нем.
Но подождите, пока вы скажете, мне нравятся мои однострочные.
Ну , Угадай, что. Код - это не просто способ управления поведением оборудования. Это способ:
Не оставляйте бомбы замедленного действия для себя
Отделяя эту функцию от двух вещей, которые она делает, нам нужен класс
class ListNeedsFives(object):
def __init__(self, b=None):
if b is None:
b = []
self.b = b
def foo():
self.b.append(5)
Выполнено
a = ListNeedsFives()
a.foo()
a.b
И почему это лучше, чем слияние всего вышеописанного кода с одной функцией.
def dontdothis(b=None):
if b is None:
b = []
b.append(5)
return b
Почему бы не сделать это?
Если вы не сработаете в своем проекте, ваш код будет жить. Скорее всего, ваша функция будет делать больше, чем это. Правильный способ создания поддерживаемого кода состоит в том, чтобы разделить код на атомные части с должным образом ограниченным объемом.
Конструктор класса является очень общепризнанным компонентом для всех, кто сделал объектно-ориентированное программирование. Размещение логики, которая обрабатывает экземпляр списка в конструкторе, делает когнитивную нагрузку понимания того, что делает код меньше.
Метод foo()
не возвращает список, почему бы и нет?
При возврате отдельного списка вы можете предположить, что безопасно делать то, что вам хочется. Но это может быть не так, поскольку он также разделяется объектом a
. Заставляя пользователя ссылаться на него как a.b
, он напоминает, где находится список. Любой новый код, который хочет изменить a.b
, естественно, будет помещен в класс, где он принадлежит.
Функция подписи def dontdothis(b=None):
не имеет ни одного из этих преимуществ.
Пильный рисунок в использовании кучи можно объяснить тем, что при вызове вызова System.out.println
создается несколько локальных переменных. В частности, в Oracle / Sun JRE несколько экземпляров HeapCharBuffer
создаются в молодом поколении, как указано в следующем снимке, полученном с использованием профилировщика памяти VisualVM:
[/g0]
Интересный бит находится в количестве живых объектов, которые присутствуют в куче. Пилообразный рисунок получается из цикла сбора мусора молодого поколения, который возникает, когда пространство эден заполняется; поскольку в программе нет тяжелой вычислительной активности, JVM может выполнять несколько итераций цикла, в результате чего заполняется пространство eden (размером 4 МБ). Последующий цикл сбора молодого поколения затем очищает большую часть мусора; это почти всегда все пространство eden, если только объекты еще не используются, о чем свидетельствует следующая следа gc, полученная из VisualVM:
[/g1]
Таким образом, поведение пилообразного шаблона может быть объяснено серией распределений объектов в быстрой последовательности, которые заполняют пространство эден, вызывая цикл сбора мусора молодого поколения; этот процесс циклически повторяется без задержек, поскольку основной процесс JVM не вытесняется другим процессом, а основной поток в JVM, который отвечает за распределение объектов, также не вытесняется другим потоком.
Есть много мест, из которых он может исходить, и это, вероятно, зависит от реализации. По крайней мере, возможны следующие (но все это просто спекуляция)
Любой процесс, распределяющий объекты с регулярной скоростью, приведет к устойчивому увеличению потребления памяти кучи, а затем мгновенным падениям, когда сборщик мусора собирает ненужные объекты, что приводит к тому, что пилообразная форма.
In в случае, если вы задаетесь вопросом, почему ваш Java-процесс сохраняет память распределения при записи на System.out
, имейте в виду, что другие потоки (например, одна из которых загружает текущую статистику памяти в JVisualVM) могут быть теми, которые выделяют память.
Фактически jVisualVM вызывает дополнительное выделение объектов. jVisualVM и jconsole используют расширения Java Management Extensions. Прикрепление к запущенному приложению и запрос JVM-метрик, вызывающих создание дополнительных объектов. Вы можете проверить это, добавив к вашему программному вызову
Runtime.getRuntime().freeMemory()
, который сообщает о свободной памяти в куче JVM. Он покажет [почти] отсутствие изменения памяти, запустив ваш код, но как только вы подключите jVisualVM к вашей программе, вы увидите увеличение использования памяти.