1

Тема: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

Всім доброго дня!
Недавно я питав, як отримати курс валют ПриватБанку. Так от написав я значить конвертер валют. Є деякі дрібні помилки, але не про то зараз.  :) Программа працює. Але я розумію, що вона то працює, але код так собі. А якось не дуже хочеться писати "код так собі". Тому хочу на прикладі такой невеликої программи спробувати оптимізувати  код - 1) зменшити кількість рядків, а по-друге в певних моментах зробити код більш лаконічним. Тому пишу сюди. Я прикриплю тут весь код і скрін, але оптимізувати хочу частинами, та і я думаю ніхто не буде продивлятись весь код, а частинами може хтось та і відповість.  :)
Тож код:

Main
# _*_ coding: utf-8 _*_

import wx
import load
import convert
import string

class Frame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, -1, u"Конвертер валют по курсу ПриватБанку",
                          size=(800, 270), pos=(20,20),
                          style=wx.DEFAULT_FRAME_STYLE^wx.MINIMIZE_BOX^wx.MAXIMIZE_BOX^wx.RESIZE_BORDER)
        # panel
        self.panel = wx.Panel(self, -1)
        
        # StaticTexts
        self.main_label = wx.StaticText(self.panel, -1,
                                        u"КОНВЕРТЕР ВАЛЮТ ПО СЬОГОДНІШНЬОМУ КУРСУ ПРИВАТБАНКУ",
                                        pos=(10,10))
        self.convert_label1 = wx.StaticText(self.panel, -1,
                                        u"Конвертуємо ", pos=(10,138))
        self.convert_label2 = wx.StaticText(self.panel, -1,
                                        u"Конвертуємо ", pos=(10,182))
        self.convert_label3 = wx.StaticText(self.panel, -1,
                                        u" в 0 гривень.", pos=(335,138))
        self.convert_label4 = wx.StaticText(self.panel, -1,
                                        u"гривень в 0", pos=(180,183))
        
        # RadioBoxs
        self.type_crn_RB = wx.RadioBox(self.panel, -1, u"Тип валюти:", (10,40),
                                    wx.DefaultSize, [u"готівка", u"не готівка"], 1,
                                    wx.RA_SPECIFY_COLS)
        self.type_oper_RB = wx.RadioBox(self.panel, -1, u"Тип операції:", (110,40),
                                    wx.DefaultSize, [u"купівля", u"продаж"], 1,
                                    wx.RA_SPECIFY_COLS)
        self.kind_crn1_RB = wx.RadioBox(self.panel, -1, u"", (180,120),
                                    wx.DefaultSize, [u"RUB", u"USD", u"EUR"], 3,
                                    wx.RA_SPECIFY_COLS)
        self.kind_crn2_RB = wx.RadioBox(self.panel, -1, u"", (320,165),
                                    wx.DefaultSize, [u"RUB", u"USD", u"EUR"], 3,
                                    wx.RA_SPECIFY_COLS)
        
        # TextCtrls
        self.value1_TC = wx.TextCtrl(self.panel, -1, "1", size=(80,20), pos=(90, 136))
        self.value2_TC = wx.TextCtrl(self.panel, -1, "1", size=(80,20), pos=(90, 182))

        # loading information from server
        self.values = load.load_currency()
        # table of currency
        string = self.string_format()
        self.table = wx.StaticText(self.panel, -1, string, pos=(400,20))
        # converting at start
        self.converting1()
        self.converting2()
        # Binds
        self.Bind(wx.EVT_RADIOBOX, self.OnConvert, self.type_crn_RB)
        self.Bind(wx.EVT_RADIOBOX, self.OnConvert, self.type_oper_RB)
        self.Bind(wx.EVT_RADIOBOX, self.OnInput1, self.kind_crn1_RB)
        self.Bind(wx.EVT_RADIOBOX, self.OnInput2, self.kind_crn2_RB)
        self.Bind(wx.EVT_TEXT, self.OnInput1, self.value1_TC)
        self.Bind(wx.EVT_TEXT, self.OnInput2, self.value2_TC)
    def string_format(self):
        return u"""     %s%s\n %s%s%s%s\n%s %15.5f %15.5f %15.5f  %15.5f\n%s%15.5f%15.5f %15.5f %15.5f\n%s%15.5f%15.5f %15.5f %15.5f""" % (u"ГОТІВКА".center(46),
                                                                                                                                        u"НЕ ГОТІВКА".center(22),
                                                                                                                    u'купівля'.rjust(21), u'продаж'.rjust(15),
                                                                                                                    u'купівля'.rjust(15), u'продаж'.rjust(15),
                                        u'RUB', self.values['RUR_cash_buy'],self.values['RUR_cash_sale'],self.values['RUR_ncash_buy'],self.values['RUR_ncash_sale'],
                                        u'USD', self.values['USD_cash_buy'],self.values['USD_cash_sale'],self.values['USD_ncash_buy'],self.values['USD_ncash_sale'],
                                        u'EUR', self.values['EUR_cash_buy'],self.values['EUR_cash_sale'],self.values['EUR_ncash_buy'],self.values['EUR_ncash_sale'])

    def OnInput1(self, event):
        value = self.value1_TC.GetValue()
        if value=="":
            pass
        else:
            if (len(value)>1) and ("." in value[:-1]) and (value[-1]=="."):
                self.value1_TC.Clear()
                self.value1_TC.AppendText(value[:-1])
                self.converting1()
            if (value=="."):
                self.value1_TC.Clear()
                self.value1_TC.AppendText("0.")

            if value[-1]==',':
                self.value1_TC.Clear()
                self.value1_TC.AppendText(value[:-1]+".")
                if len(value)==1:
                    self.value1_TC.Clear()
                else:    
                    self.converting1()
            
            else:
                if not(value[-1] in string.digits+"."):
                    
                    if len(value)==1:
                        self.value1_TC.Clear()
                    else:
                        self.value1_TC.Clear()
                        self.value1_TC.AppendText(value[:-1])
                        self.converting1()
                else:
                    self.converting1()

    def OnInput2(self, event):
        value = self.value2_TC.GetValue()
        if value=="":
            pass
        else:
            if (len(value)>1) and ("." in value[:-1]) and (value[-1]=="."):
                self.value2_TC.Clear()
                self.value2_TC.AppendText(value[:-1])
                self.converting2()
            if (value=="."):
                self.value2_TC.Clear()
                self.value2_TC.AppendText("0.")

            if value[-1]==',':
                self.value2_TC.Clear()
                self.value2_TC.AppendText(value[:-1]+".")
                if len(value)==1:
                    self.value2_TC.Clear()
                else:    
                    self.converting2()
            
            else:
                if not(value[-1] in string.digits+"."):
                    #print not(value[-1] in string.digits)
                    
                    if len(value)==1:
                        self.value2_TC.Clear()
                    else:
                        self.value2_TC.Clear()
                        self.value2_TC.AppendText(value[:-1])
                        self.converting2()
                else:
                    self.converting2()
                    

    def OnConvert(self, event):
        self.converting1()
        self.converting2()

    def converting1(self):
        list_ccr = {0:"RUR", 1:"USD", 2:"EUR"}
        
        ccr1 = list_ccr[self.kind_crn1_RB.GetSelection()]
      
        ccr1_value = float(self.value1_TC.GetValue())

        if self.type_crn_RB.GetSelection()==0:
            ccr_type = "cash"
        else:
            ccr_type = "ncash"

        if self.type_oper_RB.GetSelection()==0:
            ccr_oper = "buy"
        else:
            ccr_oper = "sale"

        temp1 = convert.ccr_to_uan(ccr1_value, ccr1, ccr_type,
                                   ccr_oper, self.values)
        self.convert_label3.SetLabel(u" в %.2f гривень." % (temp1))

    def converting2(self):
        list_ccr = {0:"RUR", 1:"USD", 2:"EUR"}
        
        ccr2 = list_ccr[self.kind_crn2_RB.GetSelection()]

        ccr2_value = float(self.value2_TC.GetValue())

        if self.type_crn_RB.GetSelection()==0:
            ccr_type = "cash"
        else:
            ccr_type = "ncash"

        if self.type_oper_RB.GetSelection()==0:
            ccr_oper = "buy"
        else:
            ccr_oper = "sale"

        temp2 = convert.uan_to_ccr(ccr2_value, ccr2, ccr_type,
                                   ccr_oper, self.values)

        self.convert_label4.SetLabel(u"гривень в %.2f" % (temp2))    

class MyApp(wx.App):
    def OnInit(self):
        self.main_frame = Frame()
        self.SetTopWindow(self.main_frame)
        self.main_frame.Show()
        return True

if __name__=="__main__":
    app = MyApp()
    app.MainLoop()
convert
def ccr_to_uan(value, ccr, type_ccr, oper, values):
    key = '_'.join([ccr, type_ccr, oper])
    return value*values[key]

def uan_to_ccr(value, ccr, type_ccr, oper, values):
    key = '_'.join([ccr, type_ccr, oper])
    return value/values[key]
load
# _*_ coding: utf-8 _*_

import json
import urllib2
import time

def load_currency():
    """функція, котра завантажує дані щодо курсу валют: \
       долар, євро та рубль, готівковкий та безготівковий \
       зі серверу ПриватБанку."""
    result = {}
    #
    print "Start loading..."
    print u"Connect to source..."
    resp1 = urllib2.urlopen("https://api.privatbank.ua/p24api/pubinfo?json&exchange&coursid=5")
    print u"Connected. Loading data..."
    data1 = resp1.read()
    print u"Done."
    data1 = json.loads(data1)
    for i in data1:
        key = '_'.join([i['ccy'],'cash','buy'])
        result[key]=i['buy']
        key = '_'.join([i['ccy'],'cash','sale'])
        result[key]=i['sale']
    print u"Waiting 2 seconds..."
    time.sleep(2)        
    #
    print u"Connect to source..."
    resp2 = urllib2.urlopen("https://api.privatbank.ua/p24api/pubinfo?exchange&json&coursid=11")
    print u"Connected. Loading data..."
    data2 = resp2.read()
    print u"Done."
    data2 = json.loads(data2)
    for i in data2:
        key = '_'.join([i['ccy'],'ncash','buy'])
        result[key]=i['buy']
        key = '_'.join([i['ccy'],'ncash','sale'])
        result[key]=i['sale']
    #
    result = float_currency(result)
    print u"ALL OK"
    return result

def float_currency(dictionary):
    #
    keys = dictionary.keys()
    for key in keys:
        dictionary[key]=float(dictionary[key])
    return dictionary

Скрін проги:
http://f1.s.сайт-злодій/gt7AZKiK.png

Переше місце, котре хочу оптимізувати - це вивід таблиці курсів:
http://f3.s.сайт-злодій/gt7AZKiL.png
Ось код для неї:

string = self.string_format()
self.table = wx.StaticText(self.panel, -1, string, pos=(400,20))
-----------------------------
def string_format(self):
    return u"""     %s%s\n %s%s%s%s\n%s %15.5f %15.5f %15.5f  %15.5f\n%s%15.5f%15.5f %15.5f %15.5f\n%s%15.5f%15.5f %15.5f %15.5f""" % (u"ГОТІВКА".center(46),
                                                                                                                                        u"НЕ ГОТІВКА".center(22),
                                                                                                                    u'купівля'.rjust(21), u'продаж'.rjust(15),
                                                                                                                    u'купівля'.rjust(15), u'продаж'.rjust(15),
                                        u'RUB', self.values['RUR_cash_buy'],self.values['RUR_cash_sale'],self.values['RUR_ncash_buy'],self.values['RUR_ncash_sale'],
                                        u'USD', self.values['USD_cash_buy'],self.values['USD_cash_sale'],self.values['USD_ncash_buy'],self.values['USD_ncash_sale'],
                                        u'EUR', self.values['EUR_cash_buy'],self.values['EUR_cash_sale'],self.values['EUR_ncash_buy'],self.values['EUR_ncash_sale'])

По-перше, як можна цей код зробити рядками не більшими ніж 80 знаків, а по-друге, можливо взагалі створення цієї таблиці зробити більш лаконічним?

Буду дуже вдячний, тим хто відгукнеться.  *SCRATCH*

2

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

A.N.Onim написав:

Переше місце, котре хочу оптимізувати - це вивід таблиці курсів:
http://f3.s.сайт-злодій/gt7AZKiL.png
Ось код для неї:

string = self.string_format()
self.table = wx.StaticText(self.panel, -1, string, pos=(400,20))
-----------------------------
def string_format(self):
    return u"""     %s%s\n %s%s%s%s\n%s %15.5f %15.5f %15.5f  %15.5f\n%s%15.5f%15.5f %15.5f %15.5f\n%s%15.5f%15.5f %15.5f %15.5f""" % (u"ГОТІВКА".center(46),
                                                                                                                                        u"НЕ ГОТІВКА".center(22),
                                                                                                                    u'купівля'.rjust(21), u'продаж'.rjust(15),
                                                                                                                    u'купівля'.rjust(15), u'продаж'.rjust(15),
                                        u'RUB', self.values['RUR_cash_buy'],self.values['RUR_cash_sale'],self.values['RUR_ncash_buy'],self.values['RUR_ncash_sale'],
                                        u'USD', self.values['USD_cash_buy'],self.values['USD_cash_sale'],self.values['USD_ncash_buy'],self.values['USD_ncash_sale'],
                                        u'EUR', self.values['EUR_cash_buy'],self.values['EUR_cash_sale'],self.values['EUR_ncash_buy'],self.values['EUR_ncash_sale'])

По-перше, як можна цей код зробити рядками не більшими ніж 80 знаків, а по-друге, можливо взагалі створення цієї таблиці зробити більш лаконічним?

Буду дуже вдячний, тим хто відгукнеться.  *SCRATCH*

Я б використовував стандартну таблицю  wx http://wxpython.org/Phoenix/docs/html/g … eBase.html

Подякували: koala, A.N.Onim2

3

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

Ви взялися за проблему не з того боку. У вас є табличка 5х5 з заголовками і об'єднаними комірками і різним вирівнюванням в комірках. Ви можете довго намагатися вирівняти її вручну, весь час стикаючися з різними хитрими ситуаціями (типу "а якщо додати ще дві валюти", "а якщо буде більше 20 знаків в комірці" чи "а якщо виникає перенесення рядка"); можете написати велосипед, який буде імітувати якийсь засіб розмітки таблиці, тоді цей код буде виглядати значно краще, але з'явиться ще величезний глюкавий шматок коду (чи не глюкавий, залежить від наполегливості); а можете просто переписати цей блок під якийсь інший, вже існуючий, віджет. Перша думка - wxHTML. Ваша таблиця виглядатиме приблизно так:

Прихований текст
<html>
  <body>
    <table>
      <tr>
        <th></th>
        <th colspan = "2">Готівковий</th>
        <th colspan = "2">Безготівковий</th>
      </tr>
      <tr>
        <th></th>
        <th>Купівля</th>
        <th>Продаж</th>
        <th>Купівля</th>
        <th>Продаж</th>
      </tr>
      <tr>
        <th>RUB</th>
        <td>1.0</td>
        <td>1.0</td>
        <td>1.0</td>
        <td>1.0</td>
      </tr>
      <tr>
        <th>USD</th>
        <td>1.0</td>
        <td>1.0</td>
        <td>1.0</td>
        <td>1.0</td>
      </tr>
      <tr>
        <th>CHF</th>
        <td>1.0</td>
        <td>1.0</td>
        <td>1.0</td>
        <td>1.0</td>
      </tr>
    </table>
  </body>
</html>

Далі буде про генерацію такого рядка (яка, очевидно, не простіша, ніж ваша). Але ввечері, вибачте.

Подякували: A.N.Onim, Arete2

4 Востаннє редагувалося koala (16.12.2015 22:33:56)

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

Так, поїхали. Отже, очевидно, що в нас буде 3 вкладені цикли - по частинах виразу USD_cash_sale:

for currency in ['','','RUR','USD','EUR']: #рядки
  #тут буде перший стовпчик із назвами валют
  for type in ['cash','ncash']:
    for direction in ['buy','sale']:

перші два значення у зовнішньому циклі - для двох рядків у заголовку. А як їх вирізнити? І куди результат буде зберігатися?

Прихований текст
result = '<html><body><table>'
for currency in ['Head','SubHead','RUR','USD','EUR']: #рядки
  if currency not in ['Head','SubHead']: #перший стовпчик
    result += '<th>%s</th>' % currency
  else:
    result += '<th></th>'
  if currency == 'Head':
    result += '<th colspan="2">' + ... + '</th>'
  elif currency == 'SubHead':
    result += '<th>' + ... + '</th>'
  else:
    for type in ['cash','ncash']:
      for direction in ['buy','sale']:
result += '</table></body></html>'

Так, звідки брати вміст для трьох крапок? Давайте додамо його до нашого словника, а ще трохи прикрасимо розгалуження за рахунок альтернативного формату і правильно їх розташуємо:
#USD_cash_sale

Прихований текст
result = '<html><body><table>'
self.values['Head_cash'] = u"ГОТІВКА"
self.values['Head_ncash'] = u"НЕ ГОТІВКА"
self.values['SubHead_buy'] = u"купівля"
self.values['SubHead_sale'] = u"продаж"
for currency in ['Head','SubHead','RUR','USD','EUR']: #рядки
  result += '<th>%s</th>\n' % ( currency if currency not in ['Head','SubHead'] else '' )
  for type in ['cash','ncash']:
    if currency == 'Head':
      result += '<th colspan="2">' + self.values[currency + '_' + type] + '</th>\n'
    else:
      for direction in ['buy','sale']:
        if currency == 'SubHead':
          result += '<th>' + self.values[currency +'_' + direction] + '</th>\n'
        else:
          result += '<td>' + self.values[currency +'_' + type + '_' + direction] + '</td>\n'
result += '</table></body></html>'

О, вже значно приємніше, як на мене. Ще трохи попрацюємо над стилем:

Прихований текст
result = '<html><body><table>'
self.values.update( { 'Head_cash':u"ГОТІВКА", 
                             'Head_ncash':u"НЕ ГОТІВКА",
                             'SubHead_buy':u"купівля",
                             'SubHead_sale':u"продаж"} )
for currency in ['Head','SubHead','RUR','USD','EUR']: #рядки
  result += '<th>%s</th>\n' % ( currency if currency not in ['Head','SubHead'] else '' )
  for type in ['cash','ncash']:
    if currency == 'Head':
      result += '<th colspan="2">%s</th>\n' % self.values['%s_%s'  % (currency, type)]
    else:
      for direction in ['buy','sale']:
        result += '<{0}>{1}</{0}>\n'.format( *( ('th',self.values['%s_%s' % (currency, direction)]) if currency == 'SubHead' else ('td',self.values['%s_%s_%s', (currency, type, direction)]) ) ) #ой, не певен, що спрацює :(
result += '</table></body></html>'

Вираз в передостанньому рядку страшний. Що можна зробити? Наприклад, загнати в self.values по 2 рази cash_buy і ncash_buy. Або розкрити його назад в традиційний if:else:. Або замінити звертання до self.values на виклик функції, який буде повертати потрібний рядок залежно від параметрів (до речі, дуже хороший спосіб). В будь-якому разі, код вже виглядає не гірше за початковий, а гнучкість його значно вища - а якщо ще трохи попрацювати, гадаю, його ще в півтора рази можна скоротити, ключові напрямки - використання альтернативної (генераторної) форми for та функції для формування тегів.

Подякували: A.N.Onim, Arete2

5 Востаннє редагувалося A.N.Onim (16.12.2015 22:24:41)

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

Ну, по-перше, дякую! По-друге, я продививсь, але розбиратись детально буду вже завтра. А по-третє, мені здається чи код для Python 3? Просто в мене 2.7.6.  :)
Як розбирусь і перепишу код, то опублікую,щоб подивились як я зрозумів, те що ви написали.
І ще раз дякую!

6 Востаннє редагувалося koala (16.12.2015 22:35:36)

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

Я не бачу тут нічого, що б потребувало третього пітона. Функція format, згідно з сайтом, з'явилася в 2.6. Втім, код я не запускав, тому гарантувати не можу. Чекаю на ваш код.

7

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

Так-с. Приступаємо до роботи.  [:}
Перше це вставив вашу таблицю в хтмл-файл і подививсь:
http://f4.s.сайт-злодій/gt7AZKj2.png
Добре. Йдемо далі.
Я вирішив спочатку в іншому скрипті зробити генерацію, а потім уже підлаштувати під прогу. Запустивши другий ваш я помітив, що у нас же не має створення рядків таблиці, тому добавив створення рядків:

# -*- coding: utf-8 -*-
result = u'<html><body><table>'
values = {}
values['Head_cash'] = u"ГОТІВКА"
values['Head_ncash'] = u"НЕ ГОТІВКА"
values['SubHead_buy'] = u"купівля"
values['SubHead_sale'] = u"продаж"
for currency in ['Head','SubHead','RUR','USD','EUR']: #рядки
    result += '<tr>'
    result += '<th>%s</th>\n' % ( currency if currency not in ['Head','SubHead'] else '' )
    for type in ['cash','ncash']:
        if currency == 'Head':
          result += '<th colspan="2">' + values[currency + '_' + type] + '</th>\n'
        else:
          for direction in ['buy','sale']:
            if currency == 'SubHead':
                result += '<th>' + values[currency+'_'+direction] + '</th>\n'
            else:
                result += '<td>' + 'Ipsum' + '</td>\n'
    result += '</tr>'            
result += '</table></body></html>'

І те що ми отримуємо:
http://f6.s.сайт-злодій/gt7AZKji.png
Оу, уже не в один рядок.  :D
Хм... не знав, що в пітоні можлива така конструкція:

(currency if currency not in ['Head','SubHead'] else '')

Гарна конструкція.
Йдемо далі по вашому посту.
Розібравши останній ваш код помітив там помилки(більш всього навмисно там залишені :P , дякую). Виправив. Не запрацювало. Тому вирішив зробити так як ви і писали - формування тегу через функцію. Ну і звичайно знову додав рядки формування <tr>. Так як це інший скрипт тому значення всі в самому скрипті тому і убрав self і так далі.
Ось, що вийшло(ще пришлось замінити в кінці RUR -> RUB):

# -*- coding: utf-8 -*-

import string

def tag(currency, direction, values,  type=''):
    if currency=='SubHead':
        return u'<{0}>{1}</{0}>'.format('th', values['%s_%s' % (currency, direction)])
    else:
        return u'<{0}>{1}</{0}>'.format('td', values['%s_%s_%s' % (currency, type, direction)])

result = '<html><body><table>'
values =  {'Head_cash':u"ГОТІВКА", 
           'Head_ncash':u"НЕ ГОТІВКА",
           'SubHead_buy':u"купівля",
           'SubHead_sale':u"продаж",
           'RUR_cash_buy': 23.65,
           'EUR_cash_buy': 23.65,
           'USD_cash_buy': 23.65,
           'RUR_ncash_buy': 23.65,
           'EUR_ncash_buy': 23.65,
           'USD_ncash_buy': 23.65,
           'RUR_cash_sale': 23.65,
           'EUR_cash_sale': 23.65,
           'USD_cash_sale': 23.65,
           'RUR_ncash_sale': 23.65,
           'EUR_ncash_sale': 23.65,
           'USD_ncash_sale': 23.65} 
for currency in ['Head','SubHead','RUR','USD','EUR']: #рядки
    result += '<tr>'
    result += '<th>%s</th>\n' % ( currency if currency not in ['Head','SubHead'] else '' )
    for type in ['cash','ncash']:
      if currency == 'Head':
          result += '<th colspan="2">%s</th>\n' % values['%s_%s'  % (currency, type)]
      else:
          for direction in ['buy','sale']:
              result += tag(currency, direction, values) if currency=='SubHead' else tag(currency, direction, values, type)
    result += '</tr>'          
result += '</table></body></html>'
result = string.replace(result, 'RUR', 'RUB')

І те, що отримав:
http://f6.s.сайт-злодій/gt7AZKjn.png

Так-с. Ось, що в мене вийшло. Чекаю ваших коментарів. Дякую.

8

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

Зайва перевірка в рядку 36 (ви її і так в функцію tag винесли).
Гадаю, щоб принципово рухатися далі, треба подумати про зміну формату зберігання даних в бік ООП.
Ну і

for direction in ['buy','sale']:
  result += tag(currency, direction, values, type)

(рядок 36 без зайвої перевірки) можна замінити на

result += ''.join(tag(currency, direction, values, type) for currency in ['buy','sale'])

це і є генераторний варіант for.

Подякували: A.N.Onim1

9

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

Змінив маємо той же самий результат  :)  - і це добре:

Код
for currency in ['Head','SubHead','RUR','USD','EUR']: #рядки
    result += '<tr>'
    result += '<th>%s</th>\n' % ( currency if currency not in ['Head','SubHead'] else '' )
    for type in ['cash','ncash']:
      if currency == 'Head':
          result += '<th colspan="2">%s</th>\n' % values['%s_%s'  % (currency, type)]
      else:
          result += ''.join(tag(currency, direction, values, type) for direction in ['buy', 'sale'])
    result += '</tr>'          
result += '</table></body></html>'
result = string.replace(result, 'RUR', 'RUB')

Але звичайно ж замінив

result += ''.join(tag(currency, direction, values, type) for currency in ['buy','sale'])

на

result += ''.join(tag(currency, direction, values, type) for direction in ['buy', 'sale'])

Дякую.

Далі буде...  :D

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

10

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

А тепер мені цей ''.join муляє очі. Може, збирати теги в список рядків, а потім робити '\n'.join() для нього? Треба подивитися...

11

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

A.N.Onim написав:

Хм... не знав, що в пітоні можлива така конструкція:

(currency if currency not in ['Head','SubHead'] else '')

Гарна конструкція.

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

Якщо цікаво, то більше таких конструкцій тут http://codeguida.com/post/315/ і ще тут дещо є http://codeguida.com/post/399/

Подякували: koala, A.N.Onim2

12

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

Дякую, дуже endwith сподобався. Звісно, форма з кількома параметрами все вирішує, але я б замість

fname = 'movie.avi'
if fname.endswith('avi') or fname.endswith('mp4'):
    print("It's a movie.")

все одно б написав

fname = 'movie.avi'
if any(fname.endswith(ending) for ending in ['avi','mp4']):
    print("It's a movie.")

13

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

koala написав:

Дякую, дуже endwith сподобався. Звісно, форма з кількома параметрами все вирішує, але я б замість

fname = 'movie.avi'
if fname.endswith('avi') or fname.endswith('mp4'):
    print("It's a movie.")

все одно б написав

fname = 'movie.avi'
if any(fname.endswith(ending) for ending in ['avi','mp4']):
    print("It's a movie.")

Перший варіант - для прикладу, щоб легше було зрозуміти. А для повсякденного програмування таки кращий другий.

14

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

Мені якраз Python тим і подобається, що те, що для повсякденного програмування в ньому зрозуміліше :)

Подякували: Q-bart2

15

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

koala написав:

А тепер мені цей ''.join муляє очі. Може, збирати теги в список рядків, а потім робити '\n'.join() для нього? Треба подивитися...

А мені подобається.  :)

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

Не розумію. От є у нас код:

numbers = [1, 2, 3, 4, 5, 6, 7]

# квадрат всіх непарних елементів
squares = [num * num for num in numbers if num % 2]

# парні числа помножити на 2, непарні - на 3
mul = [num * 3 if num % 2 else num * 2 for num in numbers]

І в першому випадку умова в кінці, а в другому на початку, але якщо в пешому випадку винести умову на початок, то виведе помилку. Чому?

16

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

Давайте розставимо дужки. Перший вираз:

squares = [(num * num) for num in numbers if num % 2]

З усіх num з numbers вибираємо ті, для яких num % 2 і їхні num * num об'єднуємо в список.

mul = [(num * 3 if num % 2 else num * 2) for num in numbers]

Для усіх num з numbers їхні (num * 3 if num % 2 else num * 2) об'єднуємо в список. А вираз в дужках, в свою чергу, означає num*3, якщо num%2, або ж num*2 в решті випадків.

Подякували: Q-bart, A.N.Onim2

17

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

Ага, тобто в першому випадку завдяки умові ми вибираємо взагалі які num from nembers ми подаємо на створення списку, а в другому уже умова слугує для дії над "витащеним" із numbers num-ом. Тобто в першом випадку умова вибору із вхідного массиву, а в другом умова обробки для вихідного. А чи можливо дві умови? Наприклад:

mul = [14 if num==5 else num for num in numbers if num % 2]

18

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

Так-с. Генерування таблиці виніс в окремий модуль:

Прихований текст
# -*- coding: utf-8 -*-

import string

def tag(currency, direction, values,  type=''):
    if currency=='SubHead':
        return u'<{0}>{1}</{0}>'.format('th', values['%s_%s' % (currency, direction)])
    else:
        return u'<{0} align="right">{1:.5f}</{0}>'.format('td', values['%s_%s_%s' % (currency, type, direction)])

def gen_table(values):
    header_dict = {'Head_cash':u'ГОТІВКА',
                   'Head_ncash':u'НЕ ГОТІВКА',
                   'SubHead_buy':u'купівля',
                   'SubHead_sale':u'продаж'}
    values.update(header_dict)
    result = '<html><body bgcolor=#ABD2FC><table cellpadding="5">'
    for currency in ['Head','SubHead','RUR','USD','EUR']: #рядки
        result += '<tr>'
        result += '<th>%s</th>\n' % ( currency if currency not in ['Head','SubHead'] else '' )
        for type in ['cash','ncash']:
            if currency == 'Head':
                result += '<th colspan="2">%s</th>\n' % values['%s_%s'  % (currency, type)]
            else:
                result += ''.join(tag(currency, direction, values, type) for direction in ['buy', 'sale'])
        result += '</tr>'          
    result += '</table></body></html>'
    result = string.replace(result, 'RUR', 'RUB')   
    return result

в головному модулі додаю цю таблицю:

Прихований текст
# table of currency
        self.html_table = wx.html.HtmlWindow(self.panel, -1, size=(335, 170), pos=(500,30), style=wx.html.HW_SCROLLBAR_NEVER)
        self.html_table.SetPage(table.gen_table(self.values))

і отримую:
http://f1.s.сайт-злодій/gt7AZKoc.png
Мені подобається.  [:}
Як вам?
------------
Далі буде...

19

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

Так. Наступний момент.
В функції завантаження даних знаходиться вивід в консоль інфи щодо кроків:

Прихований текст
def load_currency():
    """функція, котра завантажує дані щодо курсу валют: \
       долар, євро та рубль, готівковкий та безготівковий \
       зі серверу ПриватБанку."""
    result = {}
    #
    print "Start loading..."
    print u"Connect to source..."
    resp1 = urllib2.urlopen("https://api.privatbank.ua/p24api/pubinfo?json&exchange&coursid=5")
    print u"Connected. Loading data..."
    data1 = resp1.read()
    print u"Done."
    data1 = json.loads(data1)
    for i in data1:
        key = '_'.join([i['ccy'],'cash','buy'])
        result[key]=i['buy']
        key = '_'.join([i['ccy'],'cash','sale'])
        result[key]=i['sale']
    print u"Waiting 2 seconds..."
    time.sleep(2)        
    #
    print u"Connect to source..."
    resp2 = urllib2.urlopen("https://api.privatbank.ua/p24api/pubinfo?exchange&json&coursid=11")
    print u"Connected. Loading data..."
    data2 = resp2.read()
    print u"Done."
    data2 = json.loads(data2)
    for i in data2:
        key = '_'.join([i['ccy'],'ncash','buy'])
        result[key]=i['buy']
        key = '_'.join([i['ccy'],'ncash','sale'])
        result[key]=i['sale']
    #
    result = float_currency(result)
    print u"ALL OK"
    return result

Я про кучу тих прінтів. Я розумію що в самій функції, якщо вона не для виводу, ніякого виводу не повинно бути. Але виводити хочеться :-) . Як можно вийти з цієї ситуації? Я думав може через флаг котрий передається функції - 1 виводимо, 0 - ні. Але це до каждого прінта потрібна умова. Що робити?

20

Re: Оптимізація коду або Боротьба з говнокодом[Python&wxPython]

A.N.Onim написав:

Я про кучу тих прінтів. Я розумію що в самій функції, якщо вона не для виводу, ніякого виводу не повинно бути. Але виводити хочеться :-) . Як можно вийти з цієї ситуації? Я думав може через флаг котрий передається функції - 1 виводимо, 0 - ні. Але це до каждого прінта потрібна умова. Що робити?

Вам логгер потрібен. А далі logger.debug чи logger.info.

Подякували: A.N.Onim2