Friday, March 2nd, 2012
Вчера встала задача разбить 19 мегабайтный файл (с линками на профиля собранные для дальнейшего извлечения маркетинговой информации) с почти 50 000 строк на небольшие файлики по, скажем, 500 строк для последующей обработки. Сгоряча начал даже набрасывать небольшой скрипт на python, потом внезапно обнаружил что в linux есть замечательная команда – split
user@machine:~$ split --help
Usage: split [OPTION]... [INPUT [PREFIX]]
Output fixed-size pieces of INPUT to PREFIXaa, PREFIXab, ...; default
size is 1000 lines, and default PREFIX is `x'. With no INPUT, or when INPUT
is -, read standard input.
Mandatory arguments to long options are mandatory for short options too.
-a, --suffix-length=N use suffixes of length N (default 2)
-b, --bytes=SIZE put SIZE bytes per output file
-C, --line-bytes=SIZE put at most SIZE bytes of lines per output file
-d, --numeric-suffixes use numeric suffixes instead of alphabetic
-l, --lines=NUMBER put NUMBER lines per output file
--verbose print a diagnostic just before each
output file is opened
--help display this help and exit
--version output version information and exit
Пример использования:
split -d -l 450 -a 5 file.txt
Надеюсь это сэкономит кому-то время )
Posted in Linux | No Comments »
Thursday, June 9th, 2011
Сейчас трудно представить проект который бы не использовал jQuery, её используют для создания анимации, эффектов и всех этих новых веб 2.0 красивостей, которые позволяют сохранять дизайн минималистичным, но в то же время поддерживать хороший експириенс юзера от работы с сайтом.
Часто делают так :
Вы делаете так же? Перестаньте – такой подход во многом не верен, нет никаких причин размещать эти файлы у себя. Я рекомендую использовать сервис гугла Google AJAX Libraries – чтобы подгружать такой контент напрямую с (ближайших) серверов гугла.
Какие преимущества даёт такое размещение?
Уменьшается задержка (отклик) – библиотека подгружается быстрее.
Параллелизм – пока загружается jQuery, ваш сервер отдаёт остальной контент.
Кэширование : если пользователь серфил интернет – наверняка многие библиотеки с CDN гугла закэшировались браузером.
Уменьшается нагрузка на сервер – так же неплохо будет облегчить свою страницу на 30+кб
Собственно есть 2а метода :
или старый добрый
Posted in Google, html & css, Notes | 1 Comment »
Tuesday, June 7th, 2011
Многим очень нравится дизайн гитхаба, один из элементов, на который стоит обратить внимание отдельно это – кнопки (сининькие такие). Такие динамические кнопки смотрятся очень круто и с легкостью впишутся в интерьер любого сайта. Вообщем ближе к делу: в основе github buttons лежат 3и технологии – CSS, HTML и jQuery. Архив с ксс стилями и js можно скачать CSS3 Buttons, посмотреть как это выглядит – github style кнопки.
Posted in Code, html & css, OpenSource | No Comments »
Saturday, June 4th, 2011
Сервис GitHub предоставляет возможность размещать свой open source код бесплатно и использует для этого достаточно сложную систему контроля версий – git. Приватные репозитории на гитхабе доступны только для оплаченных аккаунтов. А что делать если хотелось бы скрыть код, но в то же время использовать все удобства которые предоставляют системы контроля версий?
Есть 2а пути, простой и сложный. Простой – воспользоваться сервисом который предоставляет bitbucket или поднять на сервере свой репозиторий кода.
Битбакет это примерно такая же социальная сеть для кода как и гитхаб, с той разницей что заместо git используется mercurial или hg, работать с которым как мне показалось проще. Плюс он позволяет абсолютно бесплатно создавать приватные репозитории для своих проектов код которых вы не хотите транслировать на весь веб.
Собственно, старт проекта выглядит примрно так (сначала нужно создать приватный репозиторий на bitbucket):
Start new bitbucked project
create folders etc.
>> hg init
>> hg add
make “hgrc” file in .hg folder with :
[paths]
default = https://bitbucket.org/user/project
[ui]
username = Your Name
verbose = True
also will be good idea to put .hgignore file :
# use glob syntax.
syntax: glob
# Common
*.pyc
*.pyo
*.swp
*.swo
*.orig
*~
# Project specific
.env
pip-log.txt
>> hg commit -m “Here we go”
>> hg push
.hgignore файл который задаёт как бы говорит hg не помещать в репозиторий разные ненужные файлы – бэкапы которые создаёт редактор кода, невидимые файлы и т.д.
На текущий момент кол-во приватных репозиториев на Bitbucket.org не ограничено – пользуйтесь =)
Posted in Code, Notes | 1 Comment »
Saturday, May 21st, 2011
Многие (почти все) используют такую замечательную библиотеку как curl (ну, или в нашем случае pycurl), работать с ней конечно можно – но не слишком удобно. Приходится думать о разных вещах – куках, установке параметров и т.д. а ведь это время и в конечном итоге – деньги. Как правило, на определенном этапе многие пишут свои “функции” разной степени кривости облегчающие работу с curl’ом .
Однако грамотные программисты предпочитают реюзать чужой код и не изобретать колесо, особенно в том случае если он хорошо написан и часто обновляется.
(установка в убунте – sudo pip install grab)
Grab идеально подходит для большинства типичных задач (парсинг, регистрации), фактически освобождая программиста от рутины прямой работы с курлом и значительно сокращает размер кода. Я бы сказал, что Grab в какой-то степени эмулирует браузер (естественно без js). Итак, что внутри?
- Отправка разных HTTP запросов
- Обработка HTTP ответа
- Парсинг и заполнение HTML форм
- Работа с HTML DOM через XPath.
- XPath для обхода html
Использование XPath сильно упрощает задачу выбора элементов, это гораздо более удобно чем иметь дело с мутными регулярками.
Более подробно про XPath - http://www.w3schools.com/xpath/default.asp
В качестве примера – простой код.
# выделяем каждый элемент td с классом "postbody"
for item in document.xpath('//td[@class="postbody"]//'):
print item.text
Документация по Grab достаточно подробна, примеры использования тут и тут (в этих примерах граб создается в объекте parser). Как орудовать самим грабом можно посмотреть тут (парсинг форума), пример не рабочий, сразу предупреждаю, служит только для наглядной демонстрации того как можно его использовать.
Posted in Code, OpenSource, Python | No Comments »
Tuesday, March 22nd, 2011
Очень часто нужно обработать файл и удалить дубли. Особенно это актуально при работе с кейвордами и урлами. Самый быстрый (без шуток!) способ сделать это – использовать следующий скрипт.
# -*- coding: utf-8 -*-
__author__ = "istinspring"
__version__ = "1.0"
import os
import sys
import time
import logging
from optparse import OptionParser
logger = logging.getLogger('uniqkeylist')
def uniq(seq):
"""The fastest way to unique list"""
seen = set()
seen_add = seen.add
return [ x for x in seq if x not in seen and not seen_add(x)]
def main():
usage = '%s [options] [source]' % sys.argv[0]
parser = OptionParser(usage)
parser.add_option('-d', '--dst', default='',
help='redirect logs to file')
parser.add_option("-l", "--log", default="-",
help="Write logs to file (default: stdout)")
opts, args = parser.parse_args()
# setup logging
if opts.log == "-":
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
else:
logging.basicConfig(stream=opts.log, level=logging.DEBUG)
if args:
src = os.path.abspath(args[0])
if not os.path.isfile(src):
logger.info("Can't locate file")
sys.exit(1)
else:
logger.info("Enter source filename")
sys.exit(1)
# open source file
try:
with open(src, "r+") as f:
lines = f.readlines()
except IOError:
logger.info("Can't open file")
sys.exit(1)
result = uniq(lines)
logger.info("In\t:\t{0}".format(len(lines)))
logger.info("Out\t:\t{0}".format(len(result)))
dst = opts.dst or (os.path.splitext(src)[0] + ".unq")
# save result
try:
with open(dst, "w+") as f:
for item in result:
f.write(item)
except IOError:
logger.info("Can't save file")
sys.exit(1)
start_time = time.time()
main()
print "Elapsed time:\t%s" % (time.time() - start_time)
использовать python scriptname.py имя файла
Posted in Code, Python | 2 Comments »
Friday, March 4th, 2011
Наткнулся в Линуксе на очень удобную штучку: в окне терминала можно работать сразу с несколькими процессами, посылая какие-то на фоновое выполнения и переключаясь между тем что нужно на данный момент.
Когда вы находитесь в терминале и, к примеру, у вас запущен mc, можно в любой момент послать эту задачу на фоновое выполнение нажав [ctrl+z]
Чтобы посмотреть список задач которые “висят” на фоне нужно набрать: [jobs] (без квадратных скобок конечно)
Для переключение между задачами [fg номер задачи] (где номер задачи 1, 2… такой же как и в списке который выводится после команды [jobs])
Чтобы сразу запустить задачу как фоновый процесс нужно добавить [&]. Например [mc &]
Очень удобно )
Posted in Linux, Notes | No Comments »
Wednesday, January 5th, 2011
Первый пост в новом году, поздравляю всех читателей блога с Новым Годом! Главное свежие идеи и обучаемость, бабло же как правило приложится.
Вообщем обнаружил топ 1000 от гугла, решил записать, чтобы не забыть.
You can see a list of the largest 1000 sites worldwide, based on Unique Visitors (users), as measured by Ad Planner. This list is updated monthly as new Ad Planner datasets are released. The list defines sites as top-level domains.
Posted in Advertising | 2 Comments »
Monday, December 13th, 2010
Как получить список всех папок и файлов в определенной директории? Очень просто – используя функцию os.walk()
import os
def getsubs(dir):
# get all
dirs = []
files = []
for dirname, dirnames, filenames in os.walk(dir):
dirs.append(dirname)
for subdirname in dirnames:
dirs.append(os.path.join(dirname, subdirname))
for filename in filenames:
files.append(os.path.join(dirname, filename))
return dirs, files
Posted in Code, Python | 2 Comments »
Thursday, November 25th, 2010
Бывает различные сервисы используют url-прокладку тем самым скрывая оригинальный url ссылки, а узнать оригинальный урл очень хочется. Примеров масса – различные сервисы сокращения ссылок (shorurl), ссылки feedburner через feedproxy.google.com. Короче, очень жизненный пример : мне нужно получать данные из кучи RSS, часть из них проходит через этот самый feedburner который преобразует ссылки таким образом – http://feedproxy.google.com/~r/somesite/~3/H_xFNwC1ikE/ не, ну это блядство, срочно нужна ссылка на оригинал.
Как получить оригинальный url ссылки shorturl?
Делается это элементарно, дергаем этот линк, проходим все редиректы и возвращаем тот url куда в конечном итоге пришли.
Используя urllib2. Самый простой метод, поэтому наиболее предпочтительный.
import urllib2
fp = urllib2.urlopen('http://feedproxy.google.com/~r/somesite/~3/H_xFNwC1ikE/')
print fp.geturl()
Используя httplib. Замороченный, нужно разбивать урл на части, что как бы не очень удобно.
import httplib
""" http://feedproxy.google.com/~r/somesite/~3/H_xFNwC1ikE/ """
conn = httplib.HTTPConnection('feedproxy.google.com')
conn.request('HEAD', '/~r/somesite/~3/H_xFNwC1ikE/')
response = conn.getresponse()
print response.getheader('location')
Используя культовый curl
import pycurl
conn = pycurl.Curl()
conn.setopt(pycurl.URL, 'http://feedproxy.google.com/~r/somesite/~3/H_xFNwC1ikE/')
conn.setopt(pycurl.FOLLOWLOCATION, 1)
conn.setopt(pycurl.CUSTOMREQUEST, 'HEAD')
conn.setopt(pycurl.NOBODY, True)
conn.perform()
print conn.getinfo(pycurl.EFFECTIVE_URL)
Кстати, первый спамм на .РФ – онлайн-гипермаркет.рф
Posted in Code, Notes, Python | No Comments »
Saturday, November 20th, 2010
Для тех кто ранее изучал php и привык к тому, что код как бы встраивается в страницу при переходе на Python становится актуальным вопрос : а как сделать подобное на Pyhton? Тут сделано немного не так, но тоже достаточно удобно. Дело в том, что Python изначально проектировался как полнофункциональный язык программирования, в отличии от PHP забацаного чтобы студенты могли по быстрому написать Home Page (aka хомяк). Существует несколько подходов к тому как отображать контент в веб :
- Модель статических страниц (static page model). В первой половине 90х годов все страницы веба были статическими, то есть содержали в себе готовый документ в формате HTML. Соответственно, страницы не требовали предварительной обработки и не зависели от аргументов.
- CGI — сценарии. Исполняемые на сервере программы используют для вывода результата библиотечные функции записи в стандартный вывод(print для языков С/C++, write для языка Pascal и т.п.). Код представления данных разбросан среди кода программы в аргументах вызовов этих функций.
- Модель динамических страниц (dynamic page model). Не зависящая от аргументов часть страницы пишется в HTML-коде, а функциональная часть встраивается в код страницы с помощью специальных тегов — скриптлетов.
- Модель MVC. Реализация шаблона проектирования модель-представление-контроллер (Model-View-Controller), для разделения зависимости кода представления и кода программного обеспечения, работающего с ресурсами сервера.
Последний подход все больше входит в моду и становится популярным среди web-девелоперов. Впрочем, в PHP так же есть средства (фрэймворки) позволяющие использовать эту модель по полной программе, в частности Zend Framwork.
Питон в свою очередь может похвастаться большим количеством грамотно построенных фрэймворков пользоваться которыми одно удовольсвие. Местная звезда – фрэймворк Django, на основе которого собирается сайт практически любой сложности, от форумов до блогов. И все же в чем преимущества MVC? Их несколько, одно из самых существенных – разделение кода/данных/дизайна
Ближе к делу. MVC.
Как это работает? Очень просто как следует из названия у системы 3и компонента Model, View и Controller. Каждый компонент отвечает за свою область, поэтому достаточно легко разделить усилия программистов и дизейнеров между этими частями, пусть каждый занимается своим делом.
MVC определяет способ разработки программного обеспечения при котором код для определения и доступа к данным (модель – model) отделён от логики приложения (управление – controller), которая в свою очередь отделена от интерфейса пользователя (представление – view).
Model – отвечает за представление и обработку данных. Часто используется ORM что значительно упрощает разработку.
View – отвечает за отображение информации, за представление её пользователю в удобном виде.
Controller – связывает модель и представление. Передает данные, введённые пользователем в модель другими словами является средством, при помощи которого пользователи взаимодействуют с системой.

Рассмотрим View подробнее, ведь это именно то, что визуализирует сухие данные. Для представления используют так называемые Template Engines. Выглядит это примерно так :
<title>{% block title %}{% endblock %}</title>
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>
Их достаточно много, отдельно можно выделить Jinja2 и Mako. В чистом виде движки шаблонов позволяют вам генерировать статические страницы (html код). Т.е. используя движек шаблонов можно располагая какими-то данными написать код который будет собирать готовый сайт. Нужно было сгенерировать несколько сайтов используя каталог товаров, получилось примерно так :
env = Environment(loader=FileSystemLoader(TEMPLATE_FOLDER))
sitemap_list = []
# render index pages
for page, items in zip(index_pages, grouper(shop_items, ITEMS_PER_PAGE)):
page_data = form_nav_panel(page, index_pages)
template = env.get_template('index_pages.html')
random_5 = random.sample(shop_items, 5)
html = template.render(
index_page=True,
page_data=page_data,
index_pages=index_pages,
items_in_store=len(shop_items),
current_page = page,
items=items,
random_5=random_5,
current_date=datetime.now().strftime("%d. %B %Y")
)
with open("./sites/mystaticsite/%s" % str(page['fname']), "w") as f:
f.write(html.encode("utf-8"))
sm = {
'url' : page["fname"],
'title' : "Item list : " + str(page['id'])
}
sitemap_list.append(sm)
Т.е. берется шаблон (html + тэги движка шаблонов) загружается с помощью Jinja2, далее в функцию рендера передаются нужные данные, на выходе имеем готовый html код. Получать уникальные шаблоны несложно, используя плагины вроде ScrapBook. Грабим понравившуюся страницу, меняем дизайн по вкусу, расставляем тэги. Зачастую типовые страницы отличаются друг от друга в строении, но не на много (header + content + sidebar + footer). Чтобы не копировать полностью html код, переписывая тэги, Jinja2 предоставляет такую мощную фичу как наследование.
{% extends "index.html" %}
{% block title %}Fuck you spomoni{% endblock %}
{% block page_title %}(^_^){% endblock %}
{% block navigation_bar_top %}
<div id="navigation_bar">
<h1>Items in store : {{ items_in_store }}</h1>
<span>{% if page_data['prev_page_id'] is defined %}<a href="{{ page_data['prev_page_fname'] }}" rel="prev">« Previous </a>{% else %}« Previous {% endif %}</span>
{% for item in index_pages %}
{% if item['id'] == current_page['id'] %}
<span>{{ item['id'] + 1 }}</span>
{% else %}
<span><a href="{{ item['fname'] }}">{{ item['id'] + 1 }}</a></span>
{% endif %}
{% endfor %}
<span>{% if page_data['next_page_id'] is defined %}<a href="{{ page_data['next_page_fname'] }}" rel="next"> Next »</a>{% else %}Next »{% endif %}</span>
</div>
{% endblock %}
К предыдущему примеру. Это файл представляет собой шаблон “как он есть”, невооруженным глазом можно заметить что он наследуется от index.html переопределяя некоторые блоки, об этом явно говорит строчка : {% extends “index.html” %}
Вот таким образом и происходит построение сайта используя Template Engine. Если к этому добавить еще работу с БД и контроллер получится как раз таки этот самый MVC. Однако если нет необходимости часто менять контент на сайте, лучше генерировать его на стороне сервера за раз вытягивая данные из текстовых файлов или базы данных. Генерация статических сайтов, которые порадуют гугл и юзера своей скоростью загрузки, помимо этого существенно снижая скриптовую нагрузку на сервер.
Posted in Code, Python | No Comments »
Friday, November 19th, 2010
Как правило, если вы берете VDS или Dedicated сервер, в комплект идет несколько дополнительных IP адресов, обычно 2 или 4, зависит от дата центра. Вы межете без проблем развешивать сайты на разные айпи, управляя записями DNS домена. А как запустить скрипт на не основном ip адресе сервера? Один из вариантов предложил Lorien – использовать прокси сервер – Squid. О том, как установить Squid я писал ранее. Короче говоря часто на одном ипе висит куча скриптов которые дрочат какие-то сервисы время от времени, и чтобы не натыкаться на бан или превышение лимитов API лучше всего распределить нагрузку между доступными ip адресами. Это потребует небольшой модификации скриптов, нужно будет направлять запросы через прокси. “Прозрачно” это можно сделать используя библиотеку socksipy-branch.
Чтобы перенаправлять запросы на другой ип адрес используется следующая хитрая схема : мы направляем запрос на ip:port откуда squid роутит его на айпи используя директиву tcp_outgoing_address.

Allows you to map requests to different outgoing IP addresses based on the username or source address of the user making the request.
Позволяет вам назначать запрос на другие исходящие IP адреса, основываясь на имени или адресе источника откуда юзер сделал запрос.
Осталось только настроить конфиг Squid’а нужным образом, естественно если ипов больше чем 2а лучше всего потратить 5 минут и написать скрипт который будет генерировать нужную последовательность директив :
# Говорим squid слушать нужные порты
# http_port 20000
# http_port 20001
# Создаём ACL правила
# acl p20000 myport 20000
# acl p20001 myport 20001
# С помощью созданных ACL правил выбираем нужный исходящий IP
# tcp_outgoing_address 78.109.20.226 p20000
# tcp_outgoing_address 78.109.20.227 p20001
Взял отсюда. Немного переделал приблизив скрипт к “боевым” условиям. Список айпи берет из файла ips.txt. Начальный порт задаем в start_port.
Внимание, лучше лишний раз проверить, чтобы на ипах ничего не висело. Делается это с помощью команды : netstat -tulnap
start_port = 2000
ips_list = [ item.strip() for item in open("ips.txt", "r").readlines()]
chunks = ([], [], [])
for num, ip in enumerate(ips_list):
port = start_port + num
chunks[0].append('http_port %d' % port)
chunks[1].append('acl p%d myport %d' % (port, port))
chunks[2].append('tcp_outgoing_address %s p%d' % (ip, port))
for group in chunks:
for line in group:
print line
print
print "-------------------------------\n"
Если кто знает более простой способ welcome в комменты. Я задавал это вопрос на freenode #linux (IRC канал), но практичного решения с ходу никто не предложил. Пока что единственный рабочий вариант предложил Lorien.
Posted in Code, Proxy, Python, Server | No Comments »
Wednesday, November 17th, 2010
Эти функции позволяют вам быстро делать некоторые вещи, для которых в старом добром паскале приходилось писать лишний код. Все ниженаписанное относится к так называемому функциональному программированию, луркайте подробности.
Функции map, zip и lambda в примерах.
Простая задача есть список a = [1, 2] и список b = [3, 4] одинаковой длины и нужно слить их парами. Проще простого – используя функцию zip :
a = [1,2]
b = [3,4]
print zip(a,b)
[(1, 3), (2, 4)]
или тройками :
a = [1,2]
b = [3,4]
c = [5,6]
print zip(a,b,c)
[(1, 3, 5), (2, 4, 6)]
или в более общем виде
list = [a, b, c]
print zip(*list)
[(1, 3, 5), (2, 4, 6)]
Звездочка * перед list как-бы говорит что передается список аргументов, т.е. Действовать эквивалентно тому как если бы передали a, b, c т.е. Можно даже так print zip(*[a, b, c]) результат не изменится.
Далее функция – map. Случаются ситуации, когда внезапно нужно применить какую-либо функцию к каждому элементу списка.
Нуб напишет так :
def f(x):
return x*x
nums = [1, 2, 3]
results = []
for num in nums:
results.append(f(num))
print results
Немного поизучавший мануалы так :
def f(x):
return x*x
print [ f(num) for num in nums ]
Программист сделает проще :
def f(x):
return x*x
print map(f, nums)
А вот тру-мэдскиллз кодер сделает следующим образом :
print map(lambda x: x*x, nums)
Последняя запись являет собой пример наиболее грамотного подхода. Дело в том, что когда человек пишет код как стихи в порыве вдохновения, крайне роляет скорость написания (отсюда растут корни трепетной любви многих питон кодеров к vim и emacs), а сильная сторона питона как раз в размере генерируемого кода – он очень компактный. Написать одну строчку естественно быстрее чем 7, да и изучать короткий код проще, однако написание подобного кода требует определенного навыка. Другая сторона медали – иногда про-devs очень любят писать в одну строчку целые последовательности достаточно сложных действий, да так что очень трудно понять что там происходит и что получается в конечном итоге.
Из примера понятно, что map применяет какую-либо функцию к списку и возвращает результат опять же в виде списка. Вы можете передать несколько списков, тогда функция (идущая первым параметром) должна принимать несколько аргументов (по количеству списков переданных в map) .
def f(x, y):
return x*y
a = [1,3,4]
b = [3,4,5]
print map(f, a, b)
[3, 12, 20]
Жир, правда?
Однако если списки разной длины, т.е. Один короче другого, то он будет дополнен значениями None до нужной длины. Если убрать из списка b последнее значение – пример не будет работать, т.к. В функции f произойдет попытка умножения числа на None, а в питоне строгая типизация, что кстати выгодно отличает его от php, который в подобной ситуации работал бы дальше.
Поэтому если функция f достаточно объемна, неплохо бы проверять передаваемые значения. Например ;
def f(x, y):
if (y == None):
y = 1
return x*y
Если же заместо функции стоит None – то map действует примерно так же как и zip, но если передаваемые списки разной длины в результат будет писаться None – что кстати очень уместно в некоторых моментах.
a = [1,3,4]
b = [3,4]
print map(None, a, b)
[(1, 3), (3, 4), (4, None)]
Теперь про лямбда функции в python. Они используются когда вам необходимо определить функцию, ведь часто (как в предыдущих примерах) функция настолько мала, что определять её отдельно смыла нет (ведь это лишние строчки кода и ухудшение читабельности).
Поэтому функцию можно определить “на месте” f = lambda x: x*x как бы говорит нам – принимает x, возвращает x*x
Так используя стандартные инструменты питона можно записать довольно сложные действия в одну строчку. К примеру функцию :
def f(x, y):
if (y == None):
y = 1
return x*y
можно представить как :
lambda x, y: x * (y if y is not None else 1)
А теперь хорошо бы передавать списки отсортированные по длине – len(a) > (b) – проще простого – воспользуемся функцией sorted :
sorted([a, b], key=lambda x: len(x), reverse=True)
фунция sorted принимает список значений ([a,b] = [[1,2],[2,4,5]]) и сортирует по ключу key – который у нас задан функцией len(x) – возвращающей длину списка, сортируем в порядке убывания (reverse=True)
В конечном итоге вся операция записывается таким образом :
map(lambda x, y: x * (y if y is not None else 1), *sorted([a, b], key=lambda x: len(x), reverse=True))
списки a и b могут быть разной длины и передаваться в каком угодно порядке.
Лямбда-выражения удобны для определения не очень сложных функций, которые передаются затем другим функциям.
Posted in Code, Python | 3 Comments »