Есть ли место для улучшения моего кода Python? - PullRequest
2 голосов
/ 18 февраля 2010

Я работаю над плагином Gedit, использующим Python (и PyGTK), и я действительно не очень много работал с Python, поэтому я понятия не имею, пишу ли я код Pythonic.

Весь мой собственный код содержится в __init__.py. Есть несколько других файлов, но они из внешней библиотеки, в которую я подключаюсь. Мой __init__.py выглядит следующим образом:

#
# @file __init__.py
# Does the heavy lifting behind connecting Zen Coding to Gedit.
#

import gedit, gobject, string, gtk, re, zen_core

class ZenCodingPlugin(gedit.Plugin):
    """
    A Gedit plugin to implement Zen Coding's HTML and CSS shorthand expander.

    This file adds the menu items and keyboard shortcuts to the UI and connects
    those items with the good stuff (i.e., the code expansion).
    """

    def __init__(self):
        gedit.Plugin.__init__(self)

    def activate(self, window):
        "Gedit callback: install the expansion feature into the UI"

        ui_manager = window.get_ui_manager()
        action_group = gtk.ActionGroup("GeditZenCodingPluginActions")

        # Create the GTK action to be used to connect the key combo
        # to the Zen Coding expansion (i.e., the good stuff).
        complete_action = gtk.Action(name="ZenCodingAction",
                                     label="Expand Zen code...",
                                     tooltip="Expand Zen Code in document to raw HTML",
                                     stock_id=gtk.STOCK_GO_FORWARD)

        # Connect the newly created action with key combo
        complete_action.connect("activate",
                                lambda a: self.expand_zencode(window))
        action_group.add_action_with_accel(complete_action,
                                           "<Ctrl><Shift>E")

        ui_manager.insert_action_group(action_group, 0)

        # @TODO: Figure out what these lines do
        ui_merge_id = ui_manager.new_merge_id()
        ui_manager.add_ui(ui_merge_id,
                          "/MenuBar/EditMenu/EditOps_5",
                          "ZenCoding",
                          "ZenCodingAction",
                          gtk.UI_MANAGER_MENUITEM, False)
        ui_manager.__ui_data__ = (action_group, ui_merge_id)

    def deactivate(self, window):
        "Gedit callback: get rid of the expansion feature"

        ui_manager = window.get_ui_manager()
        (action_group, ui_merge_id) = ui_manager.__ui_data__

        # Remove the UI data, action group, and UI itself from Gedit
        del ui_manager.__ui_data__
        ui_manager.remove_action_group(action_group)
        ui_manager.remove_ui(ui_merge_id)


    def expand_zencode(self, window):
        "The action which handles the code expansion itself."

        view = window.get_active_view()
        buffer = view.get_buffer()

        # Grab the current cursor position.
        cursor_iter = buffer.get_iter_at_mark(buffer.get_insert())

        # Grab the first character in the line.
        line_iter = cursor_iter.copy()
        line_iter.set_line_offset(0)

        # Grab the text from the start of the line to the cursor.
        line = buffer.get_text(line_iter, cursor_iter)

        # Find the last space in the line and remove it, setting a variable
        # 'before' to the current line.
        words = line.split(" ")
        before = words[-1].lstrip()
        if not before:
            return

        # Get the language of the current document. Second line prevents an error
        # if first line returns None.
        lang = window.get_active_document().get_language()
        lang = lang and lang.get_name()

        # Using the 'before' variable, convert it from Zen Code
        # to expanded code. If there isn't anything, just return.
        if lang == 'CSS':
            after = zen_core.expand_abbreviation(before,'css','xhtml')
        else:
            after = zen_core.expand_abbreviation(before,'html','xhtml')
        if not after:
            return

        # Grab the line's indentation and store it.
        indent = re.match(r"\s*", line).group()

        # Automatically indent the string and replace \t (tab) with the
        # correct number of spaces.
        after = zen_core.pad_string(after,indent)
        if view.get_insert_spaces_instead_of_tabs():
            tabsize = view.get_tab_width()
            spaces = " " * tabsize
            after = after.replace("\t",spaces)

        # We are currently lame and do not know how to do placeholders.
        # So remove all | characters from after.
        after = after.replace("|", "")

        # Delete the last word in the line (i.e., the 'before' text, aka the
        # Zen un-expanded code), so that we can replace it.
        word_iter = cursor_iter.copy()
        position_in_line = cursor_iter.get_line_index() - len(before)
        word_iter.set_line_index(position_in_line)
        buffer.delete(word_iter, cursor_iter)

        # Insert the new expanded text.
        buffer.insert_at_cursor(after)

Я просто спрашиваю, потому что вышеупомянутое не кажется очень объектно-ориентированным, и мне кажется плохой идеей добавить столько логики в __init__.py, но, будучи новичком в этом, я не уверен.

Есть ли место для улучшения? Если да, то как?

(Я пытаюсь уклониться от того, что на самом деле делает плагин, потому что я ищу больше обзор стиля кодирования, чем обзор логарифма, но если вам нужно увидеть код из внешней библиотеки, весь плагин на здесь )

Ответы [ 5 ]

3 голосов
/ 18 февраля 2010

Я никогда не делал плагин GEdit, поэтому я не могу комментировать проблему __init__.py, но общие замечания:

  • Не вызывает ли родительский __init__ без новых аргументов избыточность - не могли бы вы просто удалить эти две строки?

  • Вы создаете больше локальных переменных, чем я бы. Мне приходилось смотреть по сторонам, чтобы узнать, откуда взято значение или оно используется снова. Например:

    tabsize = view.get_tab_width()
    spaces = " " * tabsize
    after = after.replace("\t",spaces)
    

    может быть:

    after = after.replace("\t", " " * view.get_tab_width())
    
  • Небольшая избыточность здесь:

    if lang == 'CSS':
        after = zen_core.expand_abbreviation(before,'css','xhtml')
    else:
        after = zen_core.expand_abbreviation(before,'html','xhtml')
    

    сравните:

    after = zen_core.expand_abbreviation(before, 'css' if lang == 'CSS' else 'html', 'xhtml')
    

Кроме того, на мой взгляд, это выглядит довольно приличным кодом Python.

2 голосов
/ 18 февраля 2010

Возможно, вы хотите переместить его в файл с именем zen_plugin.py

Тогда сделай свой __init__.py

from zen_plugin import ZenCodingPlugin
1 голос
/ 18 февраля 2010

Если у вас есть только один файл, то ваша структура каталогов, вероятно, выглядит как

pluginname
`- __init__.py

В этом случае вы можете просто выровнять свой проект до

pluginname.py

Если вам позже понадобятся другие модули в вашем пакете, вы можете перейти на

pluginname
+- __init__.py
`- plugin.py

где plugin.py содержит этот класс, а в __init__.py положить

from pluginname.plugin import ZenCodingPlugin

Таким образом, все, что ранее использовало from pluginname import ZenCodingPlugin или import pluginname; pluginname.ZenCodingPlugin, все равно будет работать.

1 голос
/ 18 февраля 2010

Я бы попытался извлечь некоторые функции из длинного expand_zencode().Например, expand_tabs().В некоторой степени это дело вкуса, но всякий раз, когда я вижу «путешествие» с комментариями, указывающими на «достопримечательности», это сильный совет для рефакторинга в функции, каждая из которых имеет комментарий к документу.(Не обязательно один к одному; у меня нет энергии / знаний для подробных советов по этой функции.) Это изменение автоматически устраняет жалобу Кена на отслеживание локальных переменных.

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

В этом нет необходимости:

def __init__(self):
    gedit.Plugin.__init__(self)

Код переменной before, похоже, не соответствует ее комментарию.(И .lstrip() избыточен, не так ли?)

Иногда у вас есть пробелы между аргументами, а иногда нет;IIRC руководство по стилю Python хочет, чтобы вы постоянно использовали пробелы.(foo(x, y), а не foo(x,y).)

Комментарий вашего класса говорит: «Этот файл добавляет ...».Разве это не должно быть "Этот плагин добавляет ..."?

Подумайте о том, чтобы спросить такого рода вещи на http://refactormycode.com/.

0 голосов
/ 18 февраля 2010

Это графический код и он всегда многословен, мой лучший совет: работает ли он? хорошо. Следующая задача!

...