1

Is there something like function_exists in PHP for Python3? I am implementing something that allows users (through some web UI) to define simple rules in JSON as follows (in some weird lisp-like structure):

["_and", ["_tautology"], ["tautology"]]

and would like to turn that into a python statement, for instance these functions

import operator
from functools import reduce

def _and(*args):
    return lambda context: reduce(operator.and, [arg(context) for arg in args], True)

def _tautology(*_):
    return lambda *__: True

by turning that original JSON rule into

_and(_tautology(), _tautology())

Just out of curiousity, is ast made for this kind of task? I did this once before but I am looking for something that is scalable. Because what I did before this was practically maintaining a dictionary like follows

mapping = {'_and': _and}

and the list would keep growing, and that results in more code typed to describe what the string value means, instead of implementing them. Or I should have used another rule engine? Because one of the rule would look like

["_and", ["_equals", "fieldA", "some_value"],
         ["_equals", "fieldB", "some_other_value"]]

Assuming _equals is

def _equals(field_name, value):
    return lambda context: context[field_name] == value

so that the rule is expanded to

_and(_equals('fieldA', 'some_value'),
     _equals('fieldB', 'some_other_value'))

TL;DR

Main Question: is there something like function_exists for Python3, is ast suitable for this? Secondary Question: should I use some sort of rule engine instead?

Regarding the duplicate question report No, I am not checking if a variable exists. I want to know if there is a function that has the same name, as a string value. For example, if I have a string '_and' I want to know if there is a function named _and, not trying to figure out whether this identifier _and is actually a function.

Jeffrey04
  • 6,138
  • 12
  • 45
  • 68
  • 5
    http://stackoverflow.com/questions/843277/how-do-i-check-if-a-variable-exists-in-python – Morton Apr 01 '16 at 11:42
  • For this kind of task you should create a decorator that registers your functions automatically to the mapping based on name. Makes life a lot easier. – Ilja Everilä Apr 01 '16 at 11:45
  • Use http://stackoverflow.com/questions/9390126/pythonic-way-to-check-if-something-exists to check "Is `foobar` defined?" and then http://stackoverflow.com/a/18704793/1191425 to determine "Is `foobar` a function?" – Li-aung Yip Apr 01 '16 at 11:57
  • @ija I don't understand lol, mind to elaborate how it is done? – Jeffrey04 Apr 01 '16 at 13:55
  • @li-aung yip not applicable, I need to find out whether there is a function which has the same name as the string value, there is no variable pointing to any function – Jeffrey04 Apr 01 '16 at 13:58
  • @Jeffrey04 `def _and(...): ...` will actually also create a variable `_and` that references the function ``. So in a way those other linked questions do answer part of your own post. There's no separate function namespace ala some Lisps in python. – Ilja Everilä Apr 01 '16 at 14:08
  • i know the identifier `_and` points to the function, but I am looking for something more like `function_exists` in PHP, where the parameter is actually a string, to find if the string value matches any function that has the same name. – Jeffrey04 Apr 01 '16 at 14:12
  • @Jeffrey04 Python is not PHP. By any function do you mean that you wish to expose builtins and the like to your site users (a bad idea)? – Ilja Everilä Apr 01 '16 at 14:20
  • actually there's another step i need to do before doing the mapping check, for example, '_and' is going to be expanded to something like 'rules.boolean._and' and then I go look for the function. I just didn't include it in the original question for clarity purpose. – Jeffrey04 Apr 01 '16 at 14:24

1 Answers1

2

As Morton pointed out, you could use globals() and locals() to fetch a variable using a string containing the name.

In [32]: a = 1

In [33]: def b():
    c = 2
    print(globals()['a'])
    print(globals()['b'])
    print(locals()['c'])
   ....:     

In [34]: b()
1
<function b at 0x7f425cae3ae8>
2

But! For your task I would recommend using a decorator that registers your functions to a mapping automatically.

_mapping = {}


def register(f):
    _mapping[f.__name__] = f
    return f


@register
def _and(*args):
    return lambda context: reduce(operator.and_,
                                  [arg(context) for arg in args], True)

@register
def _tautology(*_):
    return lambda *_: True

and so your function_exists would be just

_mapping[key]

AST is suitable for inspecting syntax trees generated from parsed python source, modifying existing syntax trees and generating new ones and transpiling to python from a different language by generating syntax trees from it (to name a few uses). So in a way yes, you could generate AST from your JSON and compile that. I believe that is actually what Hy does, though not from JSON, but full blown lisp syntax.

Ilja Everilä
  • 50,538
  • 7
  • 126
  • 127