Начнем с объективных вещей:
Именованые параметры в методах c значениями по умолчанию
Чрезвычайно полезная возможность, с помощью которой ты забываешь что такое рефакторинг связанный с сигнатурой метода.
Пример: есть метод get объекта dict, который возвращает значение по ключу если оно есть, если его нет то возвращается значение по умолчанию.
Код на Java
public void V get(K key) {
return this.values.get(key);
}
public void V get(K key, V default) {
return this.values.get(key, default);
}
Код на python
def get(self, key, default=None):
return self.values.get(key, default)
Чем это хорошо? Давайте посмотрим на типичную эволюцию кода. Сначала у вас было все просто и значение по умолчанию возвращать было не нужно, потом в каком-то месте понадобилось вернуть значение по умолчанию. Что вы делаете в случае Java- либо добавляете метод, либо добавляете if-else блок вместо него и после 5-го такого блока таки добавляете метод и затеваете небольшой рефакторинг. В случае с python вы сразу же добавляете параметр со значением по умолчанию не нарушая прошлый код, просто потому что по другому никак, нет здесь перегрузки методов. Это простой пример, на практике встречаются функции с, например, 5 необязательными разнотипными параметрами и в случае Java это сводится к 2 ** 5 = 32 перегруженых функций, в python это все достигается одним методом И никакого рефакторинга.
*args, **kwargs - аргументы метода и именованные аргументы метода
Помимо возможности передать в метод аргументы которые объявлены в сигнатуре явно, в python вы имеете возможность передавать в метод все что захотите и, с другой стороны, доступаться к этим значениям внутри метода. Как это использовать?
Пример: вам нужно сделать метод который будет генерировать HTML тег IMG с набором аттрибутов, код на Java который это делает невозможен без передачи чего-то вроде hash map с набором аттрибутов (поправьте меня если я не прав) в итоге все выглядит так
Код на Java
public static String imageTag(Map attrs) {
StringBuffer res = new StringBuffer();
for (entry in attrs.entries()) {
res.append(String.format("%s='%s' ", entry.key(), entry.value()));
}
return String.format("<img %s/>", res);
}
.... Вызов метода
Mapattrs = new HashMap ();
attrs.put("src", "foo.gif");
attrs.put("width", "100");
attrs.put("height", "200")
attrs.put("alt", "бар натуральный")
out.write(Tags.imageTag(attrs));
Код на Python
def image_tag(**kwargs):
return "<img %s/>" % " ".join(["%s='%s'" % (k, v) for (k, v) in kwargs.items()])
.... Вызов метода
print image_tag(src="foo.gif", width=100, height=200, alt=u"бар натуральный")
3 строки вместо 13 всего, 1 строка вместо 6-ти для вызова, а аттрибутов у HTML тегов десятки. В байтах тоже метрика схожая.
Впечатляет? А теперь представьте что вы хотите задать размер по умолчанию 100 на 100 если он не задан.
код на Java
public static String imageTag(Map attrs) {
if (!attrs.hasKey("width")) {
attrs.put("width", "100")
}
if (!attrs.hasKey("height")) {
attrs.put("height", "100")
}
StringBuffer res = new StringBuffer();
for (entry in attrs.entries()) {
res.append(String.format("%s='%s' ", entry.key(), entry.value()));
}
return String.format("<img %s/>", res);
}
Код на Python (дважды спасибо Сергею за исправление)
def image_tag(**kwargs):
kwargs.setdefault('width', 100)
kwargs.setdefault('height', 100)
return "<img %s/>" % " ".join(["%s='%s'" % (k, v) for (k, v) in kwargs.items()"])
Мало того что дополнительного кода меньше он еще и быстрее читается, на что требуются минимальные знания языка.
Продолжение следует...
8 комментариев:
Хорошее сравнение с Java!
Жду следующих частей.
P.S. Однако, считаю Ruby - лучшим языком программирования ever made.
Это жесть:
def image_tag(width=100, height=100, **kwargs):
....kwargs.update(locals())
Корректно так:
def image_tag(**kwargs):
....kwargs.setdefault('width', 100)
....kwargs.setdefault('height', 100)
А я люблю Питон за то что функция может возвращать несколько значений как tuple
def myFunc():
return (1,2,3)
a,b,c=myFunc()
в результате a=1,b=2,c=3
Сергей, это работает я потом отфильтровываю kwargs ниже.
>>> image_tag()
"<img width='100' height='100'/>"
>>> image_tag(a=5)
"<img a='5' width='100' height='100'/>"
>>>
Хотя ваш пример логически правильнее в случае если метод нетривиален.
Я понимаю что работает, но трогать locals и globals плохой стиль.
Спасибо, исправил, так действительно правильней с точки зрения стиля.
Заскочил снова, заметил исправление.
Вы там забыли поменять список аргументов и в последней строчке лишнее условие.
А если добавить немого кунг-фу, то вот так:
def image_tag(**kw):
____kw.setdefault('width', 100)
____kw.setdefault('height', 100)
____attr_format = "%s='%s'".__mod__
____return "[img %s/]" % " ".join(map(attr_format, kw.iteritems()))
Еще раз спасибо, исправил.
кунг-фу сильно жесткое, все таки одним из достояний питона есть его хорошая читабельность.
Отправить комментарий