В очередной раз при правке кода на питоне у меня пригорело от list comprehensions.
А вот что пишут настоящие живые люди, которых никто не заставляет под дулом пистолета:
I find the list comprehension much clearer than
filter+lambda
Или:
Personally I find list comprehensions easier to read. It is more explicit what is happening from the expression
[i for i in list if i.attribute == value]as all the behaviour is on the surface not inside the filter function.
Ну давайте посмотрим, как этот much clearer way выглядит in the wild. Как-то так:
    def getSupportedTrackers(self):
        trackers = self.getTrackers()
        if not self.site.connection_server.tor_manager.enabled:
            trackers = [tracker for tracker in trackers if ".onion" not in tracker]
        trackers = [tracker for tracker in trackers if self.getAddressParts(tracker)]  # Remove trackers with unknown address
        if "ipv6" not in self.site.connection_server.supported_ip_types:
            trackers = [tracker for tracker in trackers if helper.getIpType(self.getAddressParts(tracker)["ip"]) != "ipv6"]
        return trackers
Просто сплошной [blabla for blabla in blablas if ...blabla...].
Просто в начале каждой такой строки ты должен мысленно стирать кусок [tracker for tracker in trackers if и читать, что же там дальше. И как писал Роберт Мартин в «Чистом коде», любые конструкции, которые принуждают читателя тренироваться пропускать себя мимо глаз, являются источником скрытых ошибок. Пропустив 500 раз мимо глаз типовой фрагмент кода, на 501-й раз вы пропускаете ПОЧТИ такой же фрагмент, в котором содержится ошибка. И в силу одинаковой натренированности рефлексов у всех разработчиков продукта, эта ошибка может оставаться незамеченной годами.
Давайте посмотрим, как этот же код можно преписать на лямбдах на руби:
    def getSupportedTrackers():
        trackers = @getTrackers()
        if not @site.connection_server.tor_manager.enabled
            trackers = trackers.filter {|tracker| not tracker.include? ".onion"}
        end
        trackers = trackers.filter {|tracker| @getAddressParts(tracker)}
        if not @site.connection_server.supported_ip_types.include? "ipv6"
            trackers = trackers.filter {|tracker| helper.getIpType(@getAddressParts(tracker)["ip"]) != "ipv6"}
        end
        return trackers
    end
Уже стало лучше за счёт уменьшения количества бойлерплейта, который приходится пропускать мимо. Но 3 вызова trackers.filter подряд и два идентичных вызова getAddressParts говорят нам, что этот код надо переписать.
Заметьте, что необходимость рефакторинга для устранения дублирования не была очевидна в коде с list comprehensions, потому что они за своей многословностью и нечитабельным синтаксисом скрывают суть происходящего.
Убираем дублирование:
    def getSupportedTrackers()
        tor_enabled = @site.connection_server.tor_manager.enabled
        ipv6_enabled = @site.connection_server.supported_ip_types.include? "ipv6"
        trackers = @getTrackers()
        trackers = trackers.filter {|tracker|
            if (not tor_enabled) and (tracker.include? ".onion")
                next false
            end
            address_parts = @getAddressParts(tracker)
            if not address_parts
                next false
            end
            if (not ipv6_enabled) and (helper.getIpType(address_parts["ip"]) == "ipv6")
                next false
            end
            next true
        }
        return trackers
    end
Этот код хотя и выглядит не так компактно при взгляде на экран издалека, на самом деле проще в чтении и в поддержке. Здесь нет дублирования, которое заставляет читателя многократно сверять строки, чтобы убедиться, что разработчик имел в виду именно то, что увидел читатель. А каждая строка выражает свою мысль без лишних бессмысленных слов, которые нужно отфильтровывать глазами.
P.S. Или для любителей длинных однострочников:
    def getSupportedTrackers()
        tor_enabled = @site.connection_server.tor_manager.enabled
        ipv6_enabled = @site.connection_server.supported_ip_types.include? "ipv6"
        trackers = @getTrackers()
        trackers = trackers.filter {|tracker|
            next false if (not tor_enabled) and (tracker.include? ".onion")
            address_parts = @getAddressParts(tracker)
            next false if not address_parts
            next false if (not ipv6_enabled) and (helper.getIpType(address_parts["ip"]) == "ipv6")
            next true
        }
        return trackers
    end
Но этот вариант по моему мнению хуже.
Перемещено leave из talks






