Pythonic方式指定比较运算符?

时间:2013-08-28 16:07:19

标签: jquery python django

(编辑:我得到了很多关于实现的答案(我很感激),但我更关心的是规范语法。这个用户插件(即Python / Django开发人员 - 而不是网站用户)将需要指定条件,使用我正在发明的语法。所以重写问题...在编写模型或表单类时... 。下面的语法中哪些原型Python / Django开发人员更喜欢为表单字段指定条件显示逻辑?)

我正在寻找一些建议用于最pythonic(可读,直接等)的方式来指定比较运算符以供稍后在比较中使用(通过javascript执行)。类似的东西(这只是一个想到的例子 - 还有很多其他可能的格式):

comparisons = (('a', '>', 'b'), ('b', '==', 'c'))

稍后将在Javascript中进行评估。

上下文是我正在研究一个Django应用程序(最终作为插件分发),这将要求用户以我选择的任何语法编写比较(因此有关使其成为pythonic的问题)。比较将引用表单字段,并最终将转换为javascript条件表单显示。我想一个例子是有序的:

class MyModel(models.Model):
    yes_or_no = models.SomeField...choices are yes or no...
    why = models.SomeField...text, but only relevant if yes_or_no == yes...
    elaborate_even_more = models.SomeField...more text, just here so we can have multiple conditions

    #here i am inventing some syntax...open to suggestions!!
    why.show_if = ('yes_or_no','==','yes')
    elaborate_even_more.show_if = (('yes_or_no','==','yes'),('why','is not','None'))

    #(EDIT - help me choose a syntax that is Pythonic and...Djangonic...and that makes your fingers happy to type!)
    #another alternative...
    conditions = {'why': ('yes_or_no','==','yes'), 
                  'elaborate_even_more': (('yes_or_no','==','yes'),('why','is not','None'))
                  }

    #or another alternative...
    """Showe the field whiche hath the name *why* only under that circumstance 
    in whiche the field whiche hath the name *yes_or_no* hath the value *yes*, 
    in strictest equality."""
    etc...

(挥手......使用model_form_factory()将MyModel转换为ModelForm ...收集字典中的所有“field.show_if”条件并将其作为MyModelForm.conditions附加到ModelForm ......)

现在,在模板中的一大块javascript中,MyModelForm.condtions中的每个条件都将成为一个函数,用于侦听字段值的更改,并显示或隐藏另一个字段作为响应。基本上(在伪Javascript / Jquery中):

when yes_or_no changes...
    if (yes_or_no.value == 'yes'){
        $('#div that contains *why* field).show(); }
    else {
        $('#div that contains *why* field).hide(); }

这里的目标是让最终用户在模型定义中以简单的pythonic方式指定条件显示逻辑(可能有一个选项来指定表单类的条件,我认为它更像是“Djangonic”(??),但对于我的用例,他们需要进入模型)。然后我的幕后插件将其转换为模板中的Javascript。因此,您无需编写任何Javascript即可获得条件表单显示。由于这将由python / django开发人员掌握,我正在寻找建议以最原生,最舒适的方式来指定这些条件。

5 个答案:

答案 0 :(得分:3)

这是一个想法:

import operator as op

a, b, c = 10, 7, 7

def f1():
    print 10

def f2():
    print 20

comparisons = ((a, op.gt, b, f1), (b, op.eq, c, f2))

for lhs, oper, rhs, f in comparisons:
    if oper(lhs, rhs):
        f()

=> 10
=> 20

通过适当的表示,您可以动态指定比较运算符及其相应的操作 - 作为函数实现。查看operator模块以查看可用的运算符。

答案 1 :(得分:1)

您如何看待使用django的语法进行查询?

>>> Entry.objects.filter(
...     headline__startswith='What'
... ).exclude(
...     pub_date__gte=datetime.date.today()
... ).filter(
...     pub_date__gte=datetime(2005, 1, 30)
... )

所以它可能看起来像:

class MyModel(models.Model):
    yes_or_no = models.SomeField()
    why = models.SomeField(yes_or_no='yes')
    ... or ...
    why = models.SomeField(field__yes_or_no='yes')
    ... or even ...
    why = models.SomeField(field__yes_or_no__contains='ye')

答案 2 :(得分:1)

我真的很喜欢SQLAlchemy如何使用运算符重载来处理这个问题,所以你可能想看看他们的实现是否有些想法。例如,以下是如何进行查询...

session.query(User).filter(User.name == 'fred')

如您所见,User.name == 'fred'实际上创建了一个新对象,然后可以将其转换为SQL字符串。这不需要是一个字符串,如果需要,可以将其转换为JSON对象。当然,这取决于User.name是否支持这种模型,我不认为Django的模型会这样做。

但是,你可以做类似的事情,比如......

class Wrapper(object):
    def __init__(self, prop):
        self.prop = prop

    def __eq__(self, x):
        return "{0} == '{1}'".format(self.prop, x)

def test(x, fun):
    w = Wrapper(x)
    return fun(w)

class MyModel(object):
    yes_or_no = 'yes_or_no'

print test(MyModel.yes_or_no, lambda x: x == 'yes') # "yes_or_no == 'yes'"

“Wrapper”对象是支持实际运算符重载的对象。在此示例中,我们返回一个字符串,但您可以返回另一个Wrapper对象以允许复合语句(例如(a == 2) & (b == 4))。究竟如何“包裹”对象取决于你。你也可以包装模型,使复合语句更容易......

model = f(MyModel)
print model.yes_or_no == 'yes' | model.yes_or_no == 'no'

答案 3 :(得分:1)

你想要的是一个简单的JavaScript表达式生成器。粗略草图:

import json

class JSExpr(object):
    #base class
    def gen(self):
        """Generate the js for this expression."""
        raise NotImplementedError()

class CodeDump(JSExpr):
    def __init__(self, code_str):
        self.code_str = code_str
    def gen(self):
        return self.code_str

class PyLit(JSExpr):
    """Py literal to js literal, e.g. 4 --> 4, "hi" --> '"hi"' """
    def __init__(self, py_lit):
        self.py_lit = py_lit
    def gen(self):
        return "%s" % (json.dumps(self.py_lit),)

class If(JSExpr):
    """Generate an if statement from the given JSExprs."""
    def __init__(self, comp, if_true, if_false=None):
        self.comp = comp
        self.if_true = if_true
        self.if_false = if_false
    def gen(self):
        return "if (%s) { %s; } else { %s; }" % (
            self.comp.gen(), self.if_true.gen(),
            self.if_false.gen() if self.if_false else "")

class BinOp(JSExpr):
    """Helper for common binary operations ==, >=, etc."""
    op = None
    def __init__(self, left, right):
        if self.op is None:
            raise ValueError("Must sub-class and define '.op'")
        self.left = left
        self.right = right

    def gen(self):
        return "((%s) %s (%s))" % (self.left.gen(), self.op, self.right.gen())

class Eq(BinOp): 
    op = '=='

class StrictEq(BinOp):
    op = '==='

class Gt(BinOp):
    op = '>'

class And(BinOp):
    op = '&&'

class StrContains(JSExpr):
    """Non-bin op example"""
    def __init__(self, left, right):
        self.left = left
        self.right = right

    def gen(self,):
        return "((%s).indexOf(%s) != -1)" % (self.left.gen(), self.right.gen())

然后你要做的就是以某种方式从网页上的用户输入生成JSExpr个实例,并使用If生成PyLit来插入值:< / p>

>>> print If(StrictEq(PyLit(4), PyLit([1, 2, 3])),
       CodeDump("console.log('weird things happening');")).gen()
if (((4) === ([1, 2, 3]))) { console.log('weird things happening');; } else { ; }

>>> print If(Gt(PyLit(4), PyLit(2)),
         CodeDump("alert('you did it!');"),
         CodeDump("alert('nope');")).gen()
if (((4) > (2))) { alert('you did it!');; } else { alert('nope');; }

或者您可以使用CodeDump检查变量名称。你问题的例子是:

>>> print If(Eq(CodeDump("yes_or_no.value"), PyLit("yes")),
     CodeDump("$('#div that contains *why* field').show();"),
     CodeDump("$('#div that contains *why* field').hide();")).gen()
if (((yes_or_no.value) == ("yes"))) { $('#div that contains *why* field').show();; } else { $('#div that contains *why* field').hide();; }

输出不是很漂亮但它应该有效。你可以花一些时间让它变得更好。

答案 4 :(得分:0)

这可能有点过分,但您可以使用Pyjaco工具将Python源代码编译为Javascript源代码。然后你可以用普通的Python编写你的比较。很难比本机Python更像Pythonic!