LINUX.ORG.RU

[python] Как написать понятный код


0

4

Здравствуйте.
С недавних пор увлекся питоном и честно сказать после джавы он выглядит очень странно
С самим с синтаксисом я разобрался, но все что я на нем пишу выглядит как говно.
Хочу понять что я неправильно делаю
Простой пример, пытался написать xml парсер, взял либу lxml и xml'ку следующего вида

<root>
	<server>s1</server>
	<login>test_login</login>
	<password>qwerty</password>
	
	<buildings ai="true">
		<building id="" type="" />
		<building id="3" type="" />
		<building id="5" type="" />
	</buildings>
</root>
Вроде бы все просто, есть какой то заголовок и в нем содержится список.
А вот что у меня получилось на питоне:
from lxml import etree
from common import SimpleEnum

TAG_TYPE = SimpleEnum(('TEXT', 'BUILDING'))

class Buildings:
    def __init__(self):
        self.ai = None
        self.building = []
        
    def __str__(self):
        return 'ai: ' + str(self.ai) + '; building: ' + str(self.building)
        
    
class Building:
    def __init__(self):
        self.id = None
        
    def __str__(self):
        return 'id: ' + self.id


class Config():
    def __init__(self):
        self.parse()
    
    def find(self, tag_name, type = TAG_TYPE.TEXT):
        tag = self.doc.find(tag_name)
        if tag is not None:
            if type ==  TAG_TYPE.TEXT:
                text = tag.text
                if text:
                    return text
                else:
                    raise ValueError('Тег: ' + tag_name + 'найден, но не имеет значений')
            elif type == TAG_TYPE.BUILDING:
                return self.parse_buildings(tag)
            
        else:
            raise ValueError('Не найден тег: ' + tag_name)
        
    def parse_buildings(self, tag):
        buildings = Buildings()
        ai = tag.get('ai')
        buildings.ai = False if ai is not None and 'FALSE' == ai.upper() else True
        buildings.building = []
        for building_tag in tag.findall('building'):
            building = Building()
            if building_tag.get('id'):
                building.id = building_tag.get('id')
            buildings.building.append(building)
        return buildings
                
        
    def parse(self):
        self.doc = etree.parse('config.xml')
        self.server = self.find('server')
        self.login = self.find('login')
        self.password = self.find('password')
        self.buildings = self.find('buildings', TAG_TYPE.BUILDING)
        
    def __str__(self):
        return 'server: ' + self.server + '; login: ' + self.login + '; password: ' + self.password + \
            '\nbuildings: ' + str(self.buildings)

print(Config())
И мне не понятно как уйти от этой сложности или может на питоне так и должно выглядеть?
Не понятно почему не предусмотрена нормальная конвертация str -> bool, bool -> str, что бы если пишешь bool('false') или bool('true') возвращались false и true.
Не понятно почему я могу расширить сущность бина (Buildings и Building) за его пределами. т.е. я бы мог вообще не писать объявление переменных в методах __init__ а просто написать pass и все равно бы все работало.
Не ясно почему нет такой удобной штуки как enum.
Хочется понять что я делаю не так?


Скажите пожалуйста, а по какой литературе вы учили питон? И еще - стандарт кодирования читали?

TheKnight ★★★ ()

Ну раз вбрасываешь, покажи-как этот же код выглядел был на Java и в чем были бы его преимущества?

anonymous ()

Питон такой язык, что писать на нем можно только в одном стиле, что обеспечивается теми же заданными отступами, например. Так что все твои «некрасивости» только у тебя в голове. ИМХО.

Zhbert ★★★★★ ()

1) Зачем xml, если это не сторонний конфиг?

2) Надеюсь ты знаешь про xpath

3) Приведенный код будет говном на любом языке.

baverman ★★★ ()

(на правах троллинга)

Могу рекомендовать взглянуть на скалу. Там есть хорошая поддержка XML. Все будет выглядеть гораздо проще и короче.

(конец троллинга)

dave ★★★★★ ()
Ответ на: комментарий от baverman

Держи!

Имеем на входе:

val doc =
  <root>
    <server>s1</server>
    <login>test_login</login>
    <password>qwerty</password>
	
    <buildings ai="true">
      <building id="" type="" />
      <building id="3" type="" />
      <building id="5" type="" />
    </buildings>
  </root>

Набросок обработки. Сами доведете до конца, если нужно.

case class Building(id: String, tp: String)

val buildings = 
  (doc \ "buildings" \ "building") map {b =>
     Building((b \ "@id").text, (b \ "@type").text)
  }

Выхлоп:

scala> println(buildings)
List(Building(,), Building(3,), Building(5,))
dave ★★★★★ ()
Ответ на: комментарий от dave

Ну и как бы делал я:

from lxml import etree

doc = '''
  <root>
    <server>s1</server>
    <login>test_login</login>
    <password>qwerty</password>

    <buildings ai="true">
      <building id="" type="" />
      <building id="3" type="" />
      <building id="5" type="" />
    </buildings>
  </root>
'''

print [(r.attrib['id'], r.attrib['type']) for r in etree.fromstring(doc).xpath('//building') ]
baverman ★★★ ()
Ответ на: комментарий от baverman

Нет, не XPath, но похоже. Кроме этого, поддерживается паттерн-матчинг по XML (через extractors). Вместе с остальными возможностями функционального программирования (case classes, higher-order functions, partial functions) получается очень мощная штука.

dave ★★★★★ ()
Ответ на: комментарий от dave

Нет, не XPath, но похоже.

Вообще, когда я впервые увидел скалу, то сахар для работы с xml показался несколько диковатым, ладно groovy, там сам бог велел, но чтоб в таком академичном языке…

baverman ★★★ ()
Ответ на: комментарий от baverman

Вообще-то, создатель скалы считает свой язык самым практичным из всех функциональных. Кстати, этот язык твиттер использует.

dave ★★★★★ ()

Быстренько разбираетесь с CL, пишете на нем этот пример, отлаживаете... потом возвращаетесь к своему коду на питон и он внезапно становится простым и понятным!;-)

AIv ★★★★★ ()
Ответ на: комментарий от AIv

Ты будешь удивлён, но xpath под CL работает точно так же, как и везде.

(require :xpath)
(require :cxml)
(require :cxml-stp)
(in-package :xpath)

(defvar *xml* "<root>
      <server>s1</server>
      <login>test_login</login>
      <password>qwerty</password>

      <buildings ai=\"true\">
              <building id=\"\" type=\"\" />
              <building id=\"3\" type=\"\" />
              <building id=\"5\" type=\"\" />
      </buildings>
</root>")

(defvar *doc* (cxml:parse *xml* (cxml-stp:make-builder)))


(let* ((nodes (all-nodes (evaluate "//building/@id" *doc*)))
        (ids (mapcar #'string-value nodes)))
  (format nil "~{нау фаунд буилдинг виз айди~a~^~% ~} ~^," ids))

"нау фаунд буилдинг виз айди
 нау фаунд буилдинг виз айди3
 нау фаунд буилдинг виз айди5 "
ugoday ★★★★★ ()

все что я на нем пишу выглядит как говно

Это отличительная особенность петухона, ничего с этим не поделаешь.

CARS ★★★★ ()

на java можно писать на любом языке, в том числе и на python

redixin ★★★★ ()
Ответ на: комментарий от dave

этот язык твиттер использует

Я бы не сказал, что это плюс.

buddhist ★★★★★ ()
Ответ на: комментарий от delete83

Сложность моего примера не в разборе xml-ки, а вообще о не удобности языка.
На джаве это было бы две сущности

class Buildings {
	private boolean ai;
	private List<Building> buildings;
	
	public boolean isAi() {
		return ai;
	}
	public void setAi(boolean ai) {
		this.ai = ai;
	}
	public List<Building> getBuildings() {
		return buildings;
	}
	public void setBuildings(List<Building> buildings) {
		this.buildings = buildings;
	}
}

class Building {
	private int id;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
}
Взглянув на которые четко видно что они из себя представляют.
И я не могу из вне расширить эти бины.
В Pythone сторонний код может добавлять новые сущности в класс. И взглянув на класс нельзя 100% сказать что он из себя представляет т.к. дальше по коду переменная представляющая этот класс может где то расширятся.
Например можно банально опечататься и место building.id = 5 написать building.di = 5 и ошибки не будет.
Еще в языке нету поддержки енумов, то что я назвал SimpleEnum является всего лишь
class SimpleEnum(set):
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError
Но так становится недоступным рефакторинг кода. Например если я захочу поменять
TAG_TYPE = SimpleEnum(('TEXT', 'BUILDING'))
на
TAG_TYPE = SimpleEnum(('LIST', 'BUILDING'))
Я не смогу нажать shift+alt+r вбить слово LIST и по всему проекту где используется SimpleEnum.TEXT заменить на SimpleEnum.LIST
Хочется понять я либо не правильно работаю с языком и тогда хочу понять что я делаю не так. Либо то что я написал это считается нормально и тогда можно сделать вывод что для меня этот язык подходит как удобная замена скриптам на баше, но не как язык для разработки.

n4ela ()
Ответ на: комментарий от n4ela

Все твои претензии - издержки динамической типизации. Не хочешь динамическую типизацию - не используй питон, тебя никто не заставляет.

provaton ★★★★★ ()
Ответ на: комментарий от n4ela

Можно так написать

class Build:

    @property
    def id(self):
        return self.__id

    @id.setter
    def id(self, val):
        self.__id = val


class Builds:

    @property
    def buildings(self):
        return self.__buildings
    
    @buildings.setter
    def buildings(self, val):
        self.__buildings = val

    @property
    def ai(self):
        return self.__ai

    @ai.setter
    def ai(self, val):
        self.__ai = val

Ну и заюз:

d = Build()
d.id = 5
print d.id

ds = Builds()

ds.buildings = []
ds.buildings.append(d)

for e in ds.buildings:
    print e.id
Norgat ★★★★★ ()
Ответ на: комментарий от provaton

Я переписал пример Java кода выше на геттерах-сеттерах. И то, и то искусственное, это должно быть очевидно же.

Norgat ★★★★★ ()
Ответ на: комментарий от n4ela

Да-да, Питон отвратителен. Ъ используют Lisp, ынтепрайз - Java, для понтов - Scala. Забей на Питон, он тебе не нужен, да и пацаны будут смеяться, если узнают, что ты программируешь на каком-то Петухоне.

tailgunner ★★★★★ ()
Ответ на: комментарий от n4ela

Вроде бы все что Вы сказали, является фичей динамических языков. В питоне (руби, луа и др) не то что о классе нельзя сказать что-то со 100% гарантией, а каждый объект можно менять отдельно как тебе пожелается.

Меньшая надежность по идее должна компенсироваться большей гибкостью и краткостью написанного кода.

ogronom ()
Ответ на: комментарий от Norgat

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

provaton ★★★★★ ()
Ответ на: комментарий от provaton

Я знаю это. Просто было лень чего-то выдумывать и писать доп. код в геттерах и сеттерах для более полной демонстрации их идеи.

А вообще да, согласен, можно было бы сделать как-то так:

class Build: pass
class Builds: pass

d = Build()
d.id = 5
print d.id

ds = Builds()

ds.buildings = []
ds.buildings.append(d)

for e in ds.buildings:
    print e.id

И всё бы работало. Но так человек не хотел как раз делать, как я понимаю.

Norgat ★★★★★ ()
Ответ на: комментарий от ymn

И да, и нет. Но можно сказать точно, что компилятор умеет парсить код на XML (у меня в примере определение doc).

dave ★★★★★ ()
Ответ на: комментарий от Norgat

> И всё бы работало. Но так человек не хотел как раз делать, как я понимаю.

Да тут походу никто в треде не осилил понять что и как ТС хотел сделать...

provaton ★★★★★ ()
Ответ на: комментарий от provaton

А как же хаскел?

Хаскел - это секта, поклоняющаяся инопланетянам.

tailgunner ★★★★★ ()
Ответ на: комментарий от provaton

ТС хочет программировать на петухоне как на жабке. Противоестественное желание приводит к извращённому результату.

P.S. Настоящий программист на фортране ...

ugoday ★★★★★ ()
Ответ на: комментарий от provaton

В джаве есть модификаторы доступа public и package позволяют обращаться к атрибуту напрямую находясь вне классе.

ТС хочет программировать на петухоне как на жабке.

Я как раз хочу понять как надо программировать на питоне так что бы через пару месяцев после написания кода. Не пришлось бы неделю понимать как все это работает.
Хочется выработать хороший стиль написания кода.
Вот например код

class Build: pass
class Builds: pass
и
class Buildings:
    def __init__(self):
        self.ai = None
        self.building = []
                    
class Building:
    def __init__(self):
        self.id = None
С одной стороны первый код короче, но он не дает ни какого представления о классе, по сути класс Build и Builds ни чем не отличаются, даже догадаться о том что они связаны нельзя.
Второй хотя бы дает представления о то что может содержаться в классе, при этом о связи двух классов опять же нам не известно. И мне не понятно как принято писать, пустой класс а потом его расширять или попытаться описать максимальное количество допустимых аттрибутов класса.

n4ela ()
Ответ на: комментарий от provaton

Но, специально для тебя:


In [9]: class Foo(object):
   ...:     __slots__ = ['java', 'sucks']
   ...:     

In [10]: f = Foo()

In [11]: f.java = 'something'

In [12]: f.sucks = 'indeed it sucks!'

In [13]: f.other_var = 135
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/home/nss/niceniche/RequestDesk/<ipython-input-13-3ced956ffea7> in <module>()
----> 1 f.other_var = 135

AttributeError: 'Foo' object has no attribute 'other_var'
provaton ★★★★★ ()
Ответ на: комментарий от n4ela

Вот например код

А стартовый пример на 3/4 состоит из какого-то невнятного разбора XML (который к тому же зачем-то сделан классом).

по сути класс Build и Builds ни чем не отличаются, даже догадаться о том что они связаны нельзя.

Естественно, делать пустые классы, а потом в рантайме вешать на них атрибуты - дурацкая идея. Но, с другой стороны, зачем вообще нужен класс Buildings и класс Config? Первое - просто список или ассоциативный массив, второе - функция. Я бы написал примерно так:

class Building(object):
    __slots__ = ("id")
    def __init__(id):
       self.id = id

def load_buildings(fname):
    # возвращает [Building] или {int: Building}

Хочется понять что я делаю не так?

Ты хочешь, чтобы Питон был Явой. Если для тебя недостатки Питона проистекающие из его динамической типизации, фатальны, просто не используй его.

tailgunner ★★★★★ ()

Не понятно почему не предусмотрена нормальная конвертация str -> bool, bool -> str, что бы если пишешь bool('false') или bool('true') возвращались false и true.

Матчасть - это матчасть, ее иногда знать надо. bool(«True»). Можно даже так: bool(«2 - 1 > 0»)

anonymous ()
Ответ на: комментарий от anonymous

bool(«2 - 1 > 0»)

заморочил голову этим bool

eval(«2 - 1 > 0 »)

anonymous ()
Ответ на: комментарий от anonymous

> Матчасть - это матчасть, ее иногда знать надо. bool(«True»). Можно даже так: bool(«2 - 1 > 0»)

Мухахаха, анонимчик, ну ты и отжог!

In [7]: bool("False")
Out[7]: True
In [8]: bool("5 > 10")
Out[8]: True
provaton ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.