22

我们正在尝试遵循PEP8指南来格式化我们的 Python 代码并保持在每行 80 个字符以下。

我们的 SQLAlchemy 行特别麻烦,有很多链式方法和大量复杂的参数、逻辑和嵌套函数。

使用 PEP8 的约束格式化 Python SQLAlchemy 是否有任何特定的最佳实践?

我找到的最接近的答案是here,但是我正在处理的代码要复杂得多。

4

4 回答 4

38

来到这里希望有一个更好的解决方案,但我想我更喜欢括号包装风格:

subkeyword = (
    Session.query(
        Subkeyword.subkeyword_id, 
        Subkeyword.subkeyword_word
    )
    .filter_by(subkeyword_company_id=self.e_company_id)
    .filter_by(subkeyword_word=subkeyword_word)
    .filter_by(subkeyword_active=True)
    .one()
)

这很好,很清楚,并且避免了可怕的反斜杠。

于 2013-09-23T22:31:20.040 回答
9

pep-8 discourages backslashes but for SQLAlchemy code I can't help but think they're the most readable, as you can keep each generative function at the start of its own line. If there's many arguments inside of parenthesis I'll break them out on individual lines too.

subkeyword = Session.query(
                  Subkeyword.subkeyword_id, 
                  Subkeyword.subkeyword_word
             ).\
               filter_by(subkeyword_company_id=self.e_company_id).\
               filter_by(subkeyword_word=subkeyword_word).\
               filter_by(subkeyword_active=True).\
               one()

it of course doesn't matter how complicated the code is, the indentation pattern can be carried on for any amount of code, however in Python we want to avoid excessive nesting. Usually with Query the nesting would occur because you're composing many subqueries together. So definitely construct the subqueries ahead of time:

subq = Session.query(
                Bat.id, 
                func.foo(Bat.x, Bat.y).label('foo')
               ).\
                filter(Bat.id==Bar.name).\
                correlate(Bar).\
                subquery()

subq2 = Session.query(Foo.id, Foo.bar).\
                filter_by(flag>5).\
                subquery()

result = Session.query(
                  subq.c.id,
                  subq.c.foo,
                  subq2.c.bar
                ).\
                join(subq2, 
                     and_(
                      subq.c.id > subq2.c.foo, 
                      subq.bar == subq2.id
                     )
                ).\
                order_by(subq.c.id, subq2.c.bar)

I'd welcome other opinions on the backslash thing.

于 2012-02-28T00:30:36.317 回答
4

我经常使用反斜杠,就像 zzzeek 在他的回答中指出的那样。PEP8 只是一个指导方针,当你违反它时,不要因为它而失眠!

但是,我也经常使用下面的格式类型,我在其中窃取了 zzzeek 的第一个示例,对其进行了轻微调整,然后重新格式化:

q = Session.query(
    Subkeyword.subkeyword_id, 
    Subkeyword.subkeyword_word,
)
q = q.filter_by(subkeyword_company_id=self.e_company_id)  # first filter
q = q.filter_by(subkeyword_word=subkeyword_word)  # 2nd filter
q = q.filter_by(subkeyword_active=True)

if filter_by_foo:
    q = q.filter(Subkeyword.foo == True)

# Run the query (I usually wrap in a try block)...
subkeyword = q.one()

对 q 的反复重新分配一开始似乎有点讨厌,但我已经克服了。性能影响实际上为零。这种方式的一大优势是您可以混合使用尾随注释和注释行来记录您的查询(就像我对上面无用的添加所做的那样)。带有反斜杠的链接行限制了你。

当使用大量逻辑触发的修改、嵌入式标量选择等来制定海量查询时,这种格式化方式特别干净。

作为另一个示例,我在 SQLAlchemy 中生成了一个相当大(> 150 行)的 CTE 查询,其中有很多混合了两种方法的混合逻辑、别名和标签(这对于生成的查询的可读性至关重要)。它的一个严重减少(和损坏)的版本开始如下:

cte_init = session.\
    query(
        child1.foo.label("child1_foo"),
        sa.literal(1).label("indent"),  # can comment on non-slashed lines
        child2.bar.label("child2bar"),
        #comments between non-slashed lines ok, too 
        sa.func.MAX(toplevel.baz).label("max_baz"),
    ).\
    select_from(top_level).\
    join(child1,
         child1.id == toplevel.fk_child1_id).\
    join(child2.
         child2.id == toplevel.fk_child2.id).\
    filter(top_level.name == "bogus").\
    cte(name = "cte", recursive = True)

if(use_filter_x):
    cte_init = cte_init.filter_by(x = "whatever")

# etc (no, the above doesn't make any sense)...

一般来说,如果您确保使用新操作(就像许多常见的 SQL 格式化方案所做的那样)引导您的线路,那么它仍然具有很好的可读性。也不要害怕括号内的换行符。

于 2013-05-01T01:51:52.007 回答
1

是的,无论你做什么,这些都会很讨厌,所以在某种程度上,你可以将这些结构分成更短的行,一定要这样做。

如果不能,则可以通过将整个 RHS 放在括号中来摆脱所有这些反斜杠。然后,Python 将正确解析多行结构而不使用反斜杠,但也很难说这是否更好。在这种情况下,我认为你只需要运用你最好的判断力,捏住鼻子,一头扎进去。

于 2012-02-28T18:49:14.097 回答