Уведомления

Группа в Telegram: @pythonsu

#1 Июнь 18, 2007 09:46:06

proDiva
От:
Зарегистрирован: 2007-02-15
Сообщения: 244
Репутация: +  0  -
Профиль   Отправить e-mail  

Еще одна битва с ветвлениями, долой if'ы!

Представляю на Вашу критику функцию записи суммы прописью. Уж очень не нравится код. Кому не лень, подскажите, как его сократить и улучшить.

#!/usr/bin/env python
# -*- coding: cp1251 -*-

“сумма прописью”

import string

def summa_propis(sums, fix='.'):
#создаем словари с вариантами текста
rub_ed = {
‘1’: ‘один рубль’,
‘2’: ‘два рубля’,
‘3’: ‘три рубля’,
‘4’: ‘четыре рубля’,
‘5’: ‘пять рублей’,
‘6’: ‘шесть рублей’,
‘7’: ‘семь рублей’,
‘8’: ‘восемь рублей’,
‘9’: ‘девять рублей’,
‘0’: ‘рублей’
}

rub_des = {
‘1’: ‘десять’,
‘2’: ‘двадцать’,
‘3’: ‘тридцать’,
‘4’: ‘сорок’,
‘5’: ‘пятьдесят’,
‘6’: ‘шестьдесят’,
‘7’: ‘семьдесят’,
‘8’: ‘восемьдесят’,
‘9’: ‘девяносто’,
‘0’: ‘'
}

rub_sot = {
’1': ‘сто’,
‘2’: ‘двести’,
‘3’: ‘триста’,
‘4’: ‘четыреста’,
‘5’: ‘пятьсот’,
‘6’: ‘шестьсот’,
‘7’: ‘семьсот’,
‘8’: ‘восемьсот’,
‘9’: ‘девятьсот’,
‘0’: ‘'
}

rub_tys = {
’1': ‘одна тысяча’,
‘2’: ‘две тысячи’,
‘3’: ‘три тысячи’,
‘4’: ‘четыре тысячи’,
‘5’: ‘пять тысяч’,
‘6’: ‘шесть тысяч’,
‘7’: ‘семь тысяч’,
‘8’: ‘восемь тысяч’,
‘9’: ‘девять тысяч’,
‘0’: ‘тысяч’
}

rub_mil = {
‘1’: ‘один миллион’,
‘2’: ‘два миллиона’,
‘3’: ‘три миллиона’,
‘4’: ‘четыре миллиона’,
‘5’: ‘пять миллионов’,
‘6’: ‘шесть миллионов’,
‘7’: ‘семь миллионов’,
‘8’: ‘восемь миллионов’,
‘9’: ‘девять миллионов’,
‘0’: ‘миллионов’
}
#исключения
rub_isk = {
‘10’: ‘десять’,
‘11’: ‘одиннадцать’,
‘12’: ‘двенадцать’,
‘13’: ‘тринадцать’,
‘14’: ‘четырнадцать’,
‘15’: ‘пятнадцать’,
‘16’: ‘шестнадцать’,
‘17’: ‘семнадцать’,
‘18’: ‘восемнадцать’,
‘19’: ‘девятнадцать’
}

kop_ed = {
‘1’: ‘копейка’,
‘2’: ‘копейки’,
‘3’: ‘копейки’,
‘4’: ‘копейки’
}

tmp = “”
tmp_r = “”
tmp_k = “”
rub = “”
kop = “”
rub_lst =
kop_lst =
lst_ed =
lst_sot =
lst_tys =
lst_mil =

#разделяем рубли и копейки по фиксатору, который можно задать самим
try:
rub, kop = sums.split(fix)
rub_lst = list(rub)
kop_lst = list(kop)

#разбиваем сумму рублей по 3 цифры
if len(rub_lst) > 3:
lst_ed = rub_lst
lst_sot = rub_lst
else:
lst_ed = rub_lst
if len(rub_lst) > 6:
lst_tys = lst_sot
lst_mil = lst_sot
else:
lst_tys = lst_sot

#составляем кусок с миллионами
if lst_mil != :
if len(lst_mil) == 1:
tmp_r = rub_mil.get(lst_mil)
elif len(lst_mil) == 2:
if lst_mil == ‘1’:
tmp_r = rub_isk.get(lst_mil+lst_mil)+' миллионов'
else:
tmp_r = rub_des.get(lst_mil)+rub_mil.get(lst_mil)
elif len(lst_mil) == 3:
if lst_mil == ‘1’:
tmp_r = rub_sot.get(lst_mil)+' '+ \
rub_isk.get(lst_mil+lst_mil)+' миллионов'
elif lst_mil == ‘0’ and lst_mil != ‘0’:
tmp_r = rub_sot.get(lst_mil)+' '+rub_mil.get(lst_mil)
elif lst_mil == ‘0’ and lst_mil == ‘0’:
tmp_r = rub_sot.get(lst_mil)+' миллионов'
else:
tmp_r = rub_sot.get(lst_mil)+' '+ \
rub_des.get(lst_mil)+' '+rub_mil.get(lst_mil)
tmp_r = tmp_r+' '

#составляем кусок с тысячами
if lst_tys != and lst_tys != :
if len(lst_tys) == 1:
tmp_r = tmp_r+rub_tys.get(lst_tys)
elif len(lst_tys) == 2:
if lst_tys == ‘1’:
tmp_r = tmp_r+rub_isk.get(lst_tys+lst_tys)+' тысяч'
else:
tmp_r = tmp_r+rub_des.get(lst_tys)+rub_tys.get(lst_tys)
elif len(lst_tys) == 3:
if lst_tys != ‘0’:
if lst_tys == ‘1’:
tmp_r = tmp_r+rub_sot.get(lst_tys)+' '+ \
rub_isk.get(lst_tys+lst_tys)+' тысяч'
elif lst_tys == ‘0’ and lst_tys != ‘0’:
tmp_r = tmp_r+rub_sot.get(lst_tys)+' '+rub_tys.get(lst_tys)
elif lst_tys == ‘0’ and lst_tys == ‘0’:
tmp_r = tmp_r+rub_sot.get(lst_tys)+' тысяч'
else:
tmp_r = tmp_r+rub_sot.get(lst_tys)+' '+ \
rub_des.get(lst_tys)+' '+rub_tys.get(lst_tys)
else:
if lst_tys == ‘1’:
tmp_r = tmp_r+rub_isk.get(lst_tys+lst_tys)+' тысяч'
elif lst_tys == ‘0’ and lst_tys != ‘0’:
tmp_r = tmp_r+rub_tys.get(lst_tys)
elif lst_tys == ‘0’ and lst_tys == ‘0’:
tmp_r = tmp_r
else:
tmp_r = tmp_r+rub_des.get(lst_tys)+ \
‘ ’+rub_tys.get(lst_tys)
tmp_r = tmp_r+' '

#составляем последний кусок
if lst_ed != and lst_ed != :
if len(lst_ed) == 1:
tmp_r = tmp_r+rub_ed.get(lst_ed)
elif len(lst_ed) == 2:
if lst_ed == ‘1’:
tmp_r = tmp_r+rub_isk.get(lst_ed+lst_ed)+' рублей'
else:
tmp_r = tmp_r+rub_des.get(lst_ed)+rub_ed.get(lst_ed)
elif len(lst_ed) == 3:
if lst_ed != ‘0’:
if lst_ed == ‘1’:
tmp_r = tmp_r+rub_sot.get(lst_ed)+' '+ \
rub_isk.get(lst_ed+lst_ed)+' рублей'
elif lst_ed == ‘0’ and lst_ed != ‘0’:
tmp_r = tmp_r+rub_sot.get(lst_ed)+' '+rub_ed.get(lst_ed)
elif lst_ed == ‘0’ and lst_ed == ‘0’:
tmp_r = tmp_r+rub_sot.get(lst_ed)+' рублей'
else:
tmp_r = tmp_r+rub_sot.get(lst_ed)+' '+ \
rub_des.get(lst_ed)+' '+rub_ed.get(lst_ed)
else:
if lst_ed == ‘1’:
tmp_r = tmp_r+rub_isk.get(lst_ed+lst_ed)+' рублей'
elif lst_ed == ‘0’ and lst_ed != ‘0’:
tmp_r = tmp_r+rub_ed.get(lst_ed)
elif lst_ed == ‘0’ and lst_ed == ‘0’:
tmp_r = tmp_r+' рублей'
else:
tmp_r = tmp_r+rub_des.get(lst_ed)+ \
‘ ’+rub_ed.get(lst_ed)
elif lst_ed == :
tmp_r = tmp_r+'рублей'

#составляем кусок с копейками
if len(kop_lst) == 1:
tmp_k = kop+'0 копеек'
elif len(kop_lst) == 2:
if kop_lst != ‘1’:
tmp_k = kop+' '+kop_ed.get(kop_lst, ‘копеек’)
else:
tmp_k = kop+' копеек'

except ValueError, x:
print ‘Не могу представить сумму прописью!’, x

tmp = tmp_r+' '+tmp_k #объединяем рубли с копейками
tmp_lst = list(tmp)
tmp_lst = str(tmp_lst).upper() #делаем первую букву заглавной
tmp = ‘'.join(tmp_lst)
return tmp

#print summa_propis(’1000001.00')



Офлайн

#2 Июнь 18, 2007 10:35:41

pythonwin
От:
Зарегистрирован: 2006-07-18
Сообщения: 1294
Репутация: +  0  -
Профиль   Отправить e-mail  

Еще одна битва с ветвлениями, долой if'ы!

Офлайн

#3 Июнь 18, 2007 23:13:15

j2a
От:
Зарегистрирован: 2006-06-29
Сообщения: 869
Репутация: +  1  -
Профиль   Отправить e-mail  

Еще одна битва с ветвлениями, долой if'ы!

С ветвлениями худо-бедно можно бороться так


if condition == 2:
result = action1
elif condition == 3:
result = action2
else:
result = action3


одинаково с:

actions = {2: action1, 3: action2}
result = actions.get(condition, action3)


а

if condition == 2:
result = action1
else:
result = action2

аналогично

result = (condition == 2 and action1) or action2


В pytils (особенно в pytils.dt.distance_of_time_in_words) часто используется второй прием, в YDbf - оба.

Но я бы не ставил самоцелью заменять все if'ы.

P.S. pythonwin уже пропиарил новое место ;)



Отредактировано (Июнь 18, 2007 23:16:59)

Офлайн

#4 Июнь 19, 2007 08:19:28

proDiva
От:
Зарегистрирован: 2007-02-15
Сообщения: 244
Репутация: +  0  -
Профиль   Отправить e-mail  

Еще одна битва с ветвлениями, долой if'ы!

j2a
Спасибо большое, реальный метод облегчить код.



Офлайн

#5 Июнь 19, 2007 08:39:06

balu
От:
Зарегистрирован: 2006-05-24
Сообщения: 521
Репутация: +  0  -
Профиль   Отправить e-mail  

Еще одна битва с ветвлениями, долой if'ы!

proDiva
j2a
Спасибо большое, реальный метод облегчить код.
Почитай еще “Функциональное программирование на Python” Мертца много интересного для себя откроешь.

ЗЫ когда-то эта проблема всплывала в рассылке zoperus покавыряйся в архивах. Там несколько решений найдено было.



Офлайн

#6 Июнь 19, 2007 14:20:38

j2a
От:
Зарегистрирован: 2006-06-29
Сообщения: 869
Репутация: +  1  -
Профиль   Отправить e-mail  

Еще одна битва с ветвлениями, долой if'ы!

balu
ЗЫ когда-то эта проблема всплывала в рассылке zoperus покавыряйся в архивах. Там несколько решений найдено было.
В том числе и финт ушами с массивом :)

http://itconnection.ru/pipermail/zopyrus/2005-September/080312.html



Офлайн

#7 Июнь 19, 2007 14:22:15

bialix
От:
Зарегистрирован: 2006-07-13
Сообщения: 774
Репутация: +  1  -
Профиль   Отправить e-mail  

Еще одна битва с ветвлениями, долой if'ы!

j2a
С ветвлениями худо-бедно можно бороться так
Вот именно, что худо-бедно.



Офлайн

#8 Июнь 19, 2007 14:30:15

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

Еще одна битва с ветвлениями, долой if'ы!

Если следовать Фаулеру, “дурной запах” ветвлений убирается посредством полиморфизма



Офлайн

#9 Июнь 22, 2007 16:34:30

balu
От:
Зарегистрирован: 2006-05-24
Сообщения: 521
Репутация: +  0  -
Профиль   Отправить e-mail  

Еще одна битва с ветвлениями, долой if'ы!

Посмотрел на твою и написал по новой:

# -*- coding: cp1251 -*-

ei ={1:(“один”, “два”, “три”, “четыре”, “пять”, “шесть”, “семь”, “восемь”, “девять”, “десять”,
“одинадцать”, “двенадцать”, “тринадцать”, “четырнадцать”, “пятнадцать”, “шестнадцать”,
“семнадцать”, “восемнадцать”, “девятнадцать”, “двадцать”),
2:(“”, “двадцать”, “тридцать”, “сорок”, “пятдесят”, “шестдесят”, “семдесят”, “восемдесят”, “девяносто”, “сто”,),
3:(“сто”, “двести”, “триста”, “четыреста”, “пятсот”, “шессот”, “семсот”, “восемсот”, “девятсот”)}

ei1 = {1: (“тысяча”, “тысячи”, “тысяч”),
2: (“миллион”, “миллиона”, “миллионов”),
3: (“миллиард”, “миллиарда”, “миллиардов”),
0: ('', ‘', ’')}

def __propis1000(summ, prop='', len_=3):
if len(`summ`)< len_: len_=len(`summ`)
if len_ == 0: return prop
elif int(`summ`) == 0 : return __propis1000(int(`summ`), prop=prop+' ', len_=len_-1)
elif summ <= 20: return prop + ei
else: return __propis1000(int(`summ`), prop=prop+ei[int(`summ`)-1]+' ', len_=len_-1)

def __get_cathegory(summ):
if summ=='0' or (5<= int(summ)<=9) or (len(summ)> 1 and 10<=int(summ)<=19):return 2
elif 2<=int(summ)<=4 : return 1
else : return 0

def __check_murrain(fun):
if not hasattr(fun, ‘__call__’):
raise TypeError ('The argument should be a callable')
def wrapper(arg, currency='uk'):
if arg == 0: return “ноль”
res = fun(arg)
res = res.replace(“два тысячи”, “две тысячи”)
res = res.replace(“один тысяча”, “одна тысяча”)
if currency == ‘uk’:
if len(`arg`) > 1 and 11<=int(`arg`)<=12: return res
if(`arg`=='1'): res = res+“одна”
elif (`arg`=='2'): res = res+“две”
return res
return wrapper

@__check_murrain
def propis(summ):
summ = `summ`
ln = range(len(summ), -1, -3)
sp, i = '', 0
for x in ln:
if x > 0:
num = summ
sp = __propis1000(int(num))+ ‘ ’+ei1+' ‘+sp
i += 1
return sp


if __name__ == ’__main__':
print propis(20201,)


Поддерживаются 2 валюты. Если надо рубли вызывай propis(20201, ‘ru’)



Офлайн

#10 Июнь 25, 2007 03:30:34

xonix
От:
Зарегистрирован: 2006-12-31
Сообщения: 38
Репутация: +  0  -
Профиль   Отправить e-mail  

Еще одна битва с ветвлениями, долой if'ы!

balu, не удержался и хочу прокомментировать, а точнее покритиковать Ваш код… Думаю, Ваш пример похож на то, как не стоит писать на питоне. По пунктам:

1) Логика программы очень запутана, и гораздо менее понятна, чем в корневом посте.
2) В подтверждение

res = res.replace("один тысяча", "одна тысяча")
выглядит как хак, и вероятно показывает несовершенство реализуемого Вами алгоритма.
3) именование функций и переменных вызывает недоумение
4) `summ` - вот это лучше не использовать, нужно использовать str, или repr. Конструкция с обратными ковычками сейчас считаются плохим тоном и будут исключены в Py3K.
5) Писать конструкции типа
if summ[-1]=='0' or (5<= int(summ[-1])<=9) or (len(summ)> 1 and 10<=int(summ[-2:])<=19):return 2
в одну строку не следует, т.к. делает код менее читаемым, питон то не даром основан на отступах.

Проcьба не воспринимать как наезд, а просто как дружескую критику :-)



Отредактировано (Июнь 25, 2007 03:32:01)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version