Найти - Пользователи
Полная версия: Еще одна битва с ветвлениями, долой if'ы!
Начало » Python для экспертов » Еще одна битва с ветвлениями, долой if'ы!
1 2 3
proDiva
Представляю на Вашу критику функцию записи суммы прописью. Уж очень не нравится код. Кому не лень, подскажите, как его сократить и улучшить.

#!/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')
pythonwin
посмотри http://www.pyobject.ru/projects/pytils
http://cheeseshop.python.org/pypi/pytils/0.2.1
j2a
С ветвлениями худо-бедно можно бороться так


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 уже пропиарил новое место ;)
proDiva
j2a
Спасибо большое, реальный метод облегчить код.
balu
proDiva
j2a
Спасибо большое, реальный метод облегчить код.
Почитай еще “Функциональное программирование на Python” Мертца много интересного для себя откроешь.

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

http://itconnection.ru/pipermail/zopyrus/2005-September/080312.html
bialix
j2a
С ветвлениями худо-бедно можно бороться так
Вот именно, что худо-бедно.
Андрей Светлов
Если следовать Фаулеру, “дурной запах” ветвлений убирается посредством полиморфизма
balu
Посмотрел на твою и написал по новой:

# -*- 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’)
xonix
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ьба не воспринимать как наезд, а просто как дружескую критику :-)
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB