1

Тема: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Так сталося, що свого часу я захопився ліспом (Scheme та Clojure, на останньому настворював досить чимало коду), але зараз мені більше імпонує Python, тож хотілось би якось прочитати й зрозуміти свої минулі напрацювання переписати старий код на нього.

Чого не вистачає і в чому взагалі складність? Перш за все, ліспоїдні мови є переважно функціональними, а Python, хоч і використовує чимало можливостей ФП, в основі має процедурний код. Досить сказати, що повноцінних анонімних функцій у синтасисі пітона не передбачено (лише лямбда, що може нести в собі лише один вираз) — у той час, як у ліспі вся програма може являти собою один великий вираз із включеними в нього алгоритмічними елементами. Не вистачає й ліспівських макросів (було б надто круто, якби нелісп використовував щось подібне) — таким чином, відсутні синтаксичні засоби можна замінити лише або функціями, або костилями.

Втім, не все так погано. Наприклад, можна замінити ліспівський let лямбдою:

;;SCHEME:
(let ((x 7) 
      (y 8))
      (let ((z (* x y)))
           (* x y z)))

можна переписати, використавши виклик лямбди:

# Python:
(lambda x=8, 
        y=9: 
        (lambda z=x*y:
                x*y*z)())()

Згадану вище проблему кількох інструкцій в одній лямбді можна обійти, використавши список чи кортеж (неоптимально й костильно, та все ж...):

s=(lambda:
(print('Hello, my friend!'), 
 print('What is your name?'), 
 input()) 
           [-1]) # беремо результат лише останньої інструкції

(Далі буде…)

Подякували: leofun011

2

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Згадану вище проблему кількох інструкцій в одній лямбді можна обійти, використавши список чи кортеж (неоптимально й костильно, та все ж...):

Але для чого? Чим це краще за звичайну функцію?

3 Востаннє редагувалося P.Y. (16.02.2016 17:21:04)

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

ADR написав:

Згадану вище проблему кількох інструкцій в одній лямбді можна обійти, використавши список чи кортеж (неоптимально й костильно, та все ж...):

Але для чого? Чим це краще за звичайну функцію?

Можливістю описати її безпосередньо у виразі (менше макаронності в коді, економія імен). Лямбда-костиль для let також часто потребує кількох інструкцій.

4

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Ну, по-перше, згадаємо той болючий GIL. Але це можна пережити.
По-друге, функція len - логічнічше було б її зробити методом різних послідовностей, типу sequence.len() або sequence.length().
По-третє, несумісні вітки 2 і 3, але це повинно виправитися, коли просто перейдуть на вітку 3 (на жаль, це буде не скоро, бо дистрибутиви лінукс мають купу залежностей на 2-му пайтоні)
Ну, ще можна було б сюди включити неможливість захистити свій код (бо то є інтерпретована мова), але Я вірю в ОпенСорс в усіх його проявах.

5

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

По-третє, несумісні вітки 2 і 3, але це повинно виправитися, коли просто перейдуть на вітку 3 (на жаль, це буде не скоро, бо дистрибутиви лінукс мають купу залежностей на 2-му пайтоні)

З цього випливають безліч проблем таких, як те що всі альтернативні реалізації інтерпретатора робляться в основному під Python 2

6

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

ADR написав:

По-третє, несумісні вітки 2 і 3, але це повинно виправитися, коли просто перейдуть на вітку 3 (на жаль, це буде не скоро, бо дистрибутиви лінукс мають купу залежностей на 2-му пайтоні)

З цього випливають безліч проблем таких, як те що всі альтернативні реалізації інтерпретатора робляться в основному під Python 2

В якому, проте, є можливість імпортувати з «майбутнього» фічі 3-го пітона. От якби ще в 3-му можна було імпортувати з «минулого»...

7 Востаннє редагувалося VTrim (16.02.2016 18:11:15)

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Ну це ж не PHP, щоб бути ідеальним.
Як можна в часи PHP, писати на вбогому путоні? Не розумію.

8

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

VTrim написав:

Ну це ж не PHP, щоб бути ідеальним.
Як можна в часи PHP, писати на вбогому путоні? Не розумію.

До чого тут PHP? Спроба почати холівар?

9

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

VTrim написав:

Ну це ж не PHP, щоб бути ідеальним.
Як можна в часи PHP, писати на вбогому путоні? Не розумію.

Зате Python простіше перетворити на LISP  8)

10

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Прихований текст

:D

11 Востаннє редагувалося P.Y. (16.02.2016 23:58:31)

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Відволічімося трохи від ліспу й погляньмо на сі-подібні мови (такі як C, C++, Java, PHP). У них є досить прикольний цикл for, яким можна не лише перебирати діапазон чисел, а й задавати довільні дії при ініціалізації, перевірці умови та переході на новий цикл. Пітонівський for є аналогом foreach, що робить його не менш крутим, але дещо менш універсальним. В принципі, сішний for є надлишковим, його цілком можна замінити на while з попередньою ініціалізацією (що здебільшого й роблять пітоністи в аналогічній ситуації), але такий підхід не дає доступу до деяких інших пітонівських фіч, пов'язаних з for (маю на увазі генератори колекцій — конструкції виду [f(i) for i in...]).

Коротше, мій костиль і кілька прикладів його застосування:

def cfor(first=0, cond=lambda x:True, step=lambda x:x+1):
    x=first
    while cond(x):
        yield x
        x=step(x)
        
if __name__=='__main__':
    print (list(cfor(1, lambda x:x<=30, lambda x:x+2)))
    print (list(cfor((1,1), lambda x:x[1]<10, lambda x:(-x[0],x[1]+1))))
    print (list(zip(cfor(step=int.__invert__), 'qwerty')))
    for i in cfor():
        if i>10: break
        print(i)

Функція сfor за заданими параметрами генерує ітератор, який можна використати як у циклі for (роблячи його функціонально подібним до сішного for), так і будь-де.

Подякували: leofun011

12 Востаннє редагувалося P.Y. (16.02.2016 22:22:22)

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

По-друге, функція len - логічнічше було б її зробити методом різних послідовностей, типу sequence.len() або sequence.length()

Ця функція є лише врапером до метода, який цілком можна використовувати напряму́:

>>> 'qwertty'.__len__()
7
>>> [1,3,5,7,9].__len__()
5
>>> {1,3,5,7,9}.__len__()
5
>>> {1:3,5:7,9:0}.__len__()
3
>>> (1,3,5,7,9).__len__()
5

P. S. Мені більше заважає протилежна проблема: методи не можна використовувати як аналоги функцій, не прив'язуючись до конкретного класу й обходячись без враперів чи лямбд. Тобто, ось так можна:

(list(map (int.__neg__, [1,2,3,4,5])))

і так теж:

(list(map (float.__neg__, [1.1,2.2,3.3,4.4,5.5])))

а для різнотипних даних це не працює (і де ваш хвалений дактайп, коли він був би так корисний?), треба вже робити так:

(list(map (lambda x:x.__neg__(), [1,2.2,3,4.4,5])))

а хотілось би так:

(list(map (.__neg__, [1,2.2,3,4.4,5])))

13

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Перезавантажувані функції. Декоратор та приклади його використання

def overload(f):
    def decor(f1):
        def call(*args, **kwargs):
            try:
                return f(*args, **kwargs)
            except TypeError:
                return f1(*args, **kwargs)
        call.__doc__=f.__doc__
        return call
    return decor
        
@overload(overload)
def overload(f1, f2):
    return overload(f1)(f2)
        
@overload(overload)    
def overload(f1, f2, *fs):
    return overload(overload(f1, f2), *fs)

if __name__=='__main__':
    def fn():
        print('no args')
    @overload(fn)
    def fn(x):
        print('one arg:', x)
    @overload(fn)
    def fn(x, y):
        print('two args:', x, y)
    @overload(fn)
    def fn(*args, **kwargs):
        print('many args:', args, kwargs)
    fn()
    fn(10)
    fn(10, 20)
    fn(1, 2, 3, a='q', b='p')
    f=overload(
        lambda x:       x,
        lambda x, y:    x*y,
        lambda x, y, z: x*y**z)
    print(f(10), f(2, 3, 4), f(3, 3))

Власне перевірку типів аргументів декоратор overload не здійснює — лише послідовно намагається викликати всі версії функції, починаючи з першої, доки список параметрів не виявиться сумісним. Втім, оскільки для перевірки він відловлює TypeError, ніщо не заважає нам зробити перевірку типів усередині функції й викинути TypeError, якщо тип не підійшов.
Недоліком такого підходу є непередбачувана робота функції, якщо невідловлений TypeError виникне в її тілі — цього слід уникати.

Подякували: leofun011

14 Востаннє редагувалося quez (18.02.2016 17:11:26)

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Чим таке "перевантаження" відрізняється від оголошення з змінною кількістю аргументів?

15

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

quez написав:

Чим таке "перевантаження" відрізняється від оголошення з змінною кількістю аргументів?

Тим, що нам не треба руками писати if len(args)==2: ... elif len(args)==3 і т.д., менше мороки з видобуванням аргументів у кожній гілці. Загалом, це дає приблизно такі ж синтаксичні можливості, що й у Scheme чи Clojure

16

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

P.Y. написав:
quez написав:

Чим таке "перевантаження" відрізняється від оголошення з змінною кількістю аргументів?

Тим, що нам не треба руками писати if len(args)==2: ... elif len(args)==3 і т.д., менше мороки з видобуванням аргументів у кожній гілці. Загалом, це дає приблизно такі ж синтаксичні можливості, що й у Scheme чи Clojure

Замість того треба руками писати def overload(f): ... def overload(f1, f2): ... . Так собі перевага.

17 Востаннє редагувалося P.Y. (18.02.2016 18:23:04)

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

quez написав:
P.Y. написав:
quez написав:

Чим таке "перевантаження" відрізняється від оголошення з змінною кількістю аргументів?

Тим, що нам не треба руками писати if len(args)==2: ... elif len(args)==3 і т.д., менше мороки з видобуванням аргументів у кожній гілці. Загалом, це дає приблизно такі ж синтаксичні можливості, що й у Scheme чи Clojure

Замість того треба руками писати def overload(f): ... def overload(f1, f2): ... . Так собі перевага.

Можна винести їх у модуль, а тоді робити імпорт — у чому проблема?


Власне, overload з кількома аргументами непотрібен, якщо ми використовуємо його лише як декоратор.
Просто мені здалася цікавою ідея зробити сам overload перезавантажуваним, а заодно заімплементити варіант overload для роботи з кількома лямбдами (як в останньому прикладі).

18

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

P.Y. написав:
quez написав:

Чим таке "перевантаження" відрізняється від оголошення з змінною кількістю аргументів?

Тим, що нам не треба руками писати if len(args)==2: ... elif len(args)==3 і т.д., менше мороки з видобуванням аргументів у кожній гілці. Загалом, це дає приблизно такі ж синтаксичні можливості, що й у Scheme чи Clojure

Ну так передавайте параметри словником. Взагалі, навіщо придумувати те, для чого мова не призначена? Так можна до будь-якої мови доколупатися.

Прихований текст
Подякували: leofun01, /KIT\2

19

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Ну так передавайте параметри словником.

Як це рятує у випадках, коли різні набори аргументів вимагають різної роботи функції?

Взагалі, навіщо придумувати те, для чого мова не призначена?

Див. вище. Портування коду, написаного на Clojure. Щодо «не призначена» — Python же ніби багатопарадигматичний, у т.ч., включає можливості ФП — то чом би для даної задачі не використати їх, трохи доповнивши?

20

Re: Чого вам не вистачає в Пітоні? Ділимося власними костилями

Any sufficiently complicated C or Fortran program contains an  ad-hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp

Python крутий тим, що перетворення його на LISP можна зробити мінімумом зусиль :)