Oh, the Horror! (Magie mit Symbolen)

Code-Stücke können hier veröffentlicht werden.
Antworten
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Wenn man in Python mit Symbolen umgeht, zB. in Sympy, dann muss das nach Pythons eigenen Regeln geschehen. Symbolische Variablen sind in Python einfach ganz normale Variablen. Deswegen muss man Symbole explizit erzeugen:

Code: Alles auswählen

>>> from sympy import *
>>> x + 1
Traceback (most recent call last):
...
NameError: name 'x' is not defined 
>>> x = symbols('x')
>>> x + 1
x + 1 
Die Zeile x = symbols('x') ist nun nicht besonders schön. Besser wäre vielleicht sowas:

Code: Alles auswählen

>>> from ... import x
>>> x + 1
x + 1 
Dazu müsste es aber ein ... geben, das jeden möglichen Variablennamen schon vordefiniert hat. Das geht also nicht. Oder doch?

Code: Alles auswählen

__all__ = []

def __load__():

    import imp
    import sys
    import types
    from sympy import symbols

    class SymbolCreator(types.ModuleType):

        def __init__(self):
            import test
            self.__name__ = 'test.symbols'
            self.__file__ = test.__file__

        def __getattr__(self, name):
            return symbols(name)

    class SymbolsImporter:

        def find_module(self, fullname, path):
            if fullname == 'test.symbols' and path == ['test']:
                return self

        def load_module(self, fullname):
            if fullname in sys.modules:
                module = sys.modules[fullname]
            else:
                module = sys.modules[fullname] = imp.new_module(fullname)
                sys.modules[fullname] = SymbolCreator()
            module.__loader__ = self
            return module

    sys.meta_path.insert(0, SymbolsImporter())

    import test.symbols

__load__()

del __load__
Und dann:

Code: Alles auswählen

$ python3
Python 3.2.3 (default, Jul 23 2012, 16:48:24)
[GCC 4.5.3] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from test.symbols import x
>>> x + 1
x + 1
>>> from test.symbols import a, b, c
>>> a ** 2 + 3 * b + c
a**2 + 3*b + c
Na also. Der einzige Haken dabei ist, das test ein Package sein muss und das Modul oben dessen __init__.py. Warum das so ist habe ich noch nicht verstanden.

SymbolCreator erbt nur von types.ModuleType damit derlei Anzeigen stimmen:

Code: Alles auswählen

>>> import pprint, sys
>>> pprint.pprint(sys.modules)
{'__future__': <module '__future__' from '/usr/lib/python3.2/__future__.py'>,
...
 'test': <module 'test' from 'test/__init__.py'>,
 'test.symbols': <module 'test.symbols' from 'test/__init__.py'>,
...
 'zipimport': <module 'zipimport' (built-in)>}
Für die reine Funktionalität bräuchte man es nicht. Jedes Objekt kann als Modul fungieren, sofern man entsprechende Finder/Loader/Importer in den Import-Mechanismus einhängt.
In specifications, Murphy's Law supersedes Ohm's.
Benutzeravatar
bwbg
User
Beiträge: 407
Registriert: Mittwoch 23. Januar 2008, 13:35

Ob man nun Symbole mittels Funktion erzeugt oder pseudo-importiert ändert doch nichts an der "Unschönheit". Mit gefällt x = symbol('x'), trotz der Redundanz besser, als die import-Variante. collections.namedtuple macht es genau so.

Mit wäre bedingtes tuple-unpacking lieber:

Code: Alles auswählen

a, b, c = symbols()
Aber für den begrenzten Nutzen muss man nicht unbedingt die Sprache anpassen.
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
BlackJack

Also zumindest für die gängigen Namen, also das Alphabet sowohl gross, als auch klein, und ausgeschriebene griechische Buchstaben, gibt es ja schon das `sympy.abc`-Modul:

Code: Alles auswählen

In [1]: from sympy.abc import 
A        K        T        beta     gamma    n        r        w
B        L        U        c        h        nu       rho      x
C        M        V        chi      i        o        s        xi
D        N        W        d        iota     omega    sigma    y
E        O        X        delta    j        omicron  t        z
F        P        Y        e        k        p        tau      zeta
G        Q        Z        epsilon  kappa    phi      theta    
H        R        a        eta      l        pi       u        
I        S        alpha    f        m        psi      upsilon  
J        Symbol   b        g        mu       q        v        

In [1]: from sympy.abc import a, b, c
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@bwbg: Mit "nicht besonders schön" meinte ich, dass man leicht, zB. wenn man unaufmerksam Code umbaut, bei sowas landen kann:

Code: Alles auswählen

>>> x = symbols('y')
>>> y = symbols('x')
>>> 2*x + 3*y
3*x + 2*y
Besser ist es, wenn man bei etwas, das logisch betrachtet etwas einzelnes ist, auch nur etwas einzelnes tun muss. In diesem Fall ist das einzelne die Benennung.

Du schreibst, man müsse nicht für den begrenzten Nutzen die Sprache anpassen. Einen bestehenden Mechanismus der Sprache zu verwenden, wie ich das gemacht habe, ist gerade keine Anpassung der Sprache. Dein Vorschlag mit dem tuple unpacking dagegen würde mindestens erfordern, dass die Funktion symbols() weiß, welcher Code sie gerade aufruft, damit sie dort nachsehen kann, welche und wieviele Namen sie erzeugen muss. Oder man müsste an der Sprachdefinition von Python rumdoktern. Das wäre alles nicht nur wesentlich komplizierter, sondern eben auch eine Änderung dessen, wie Python funktioniert. Also eine Anpassung der Sprache.

@BlackJack: Meine Motivation für diesen Code stammt nur nicht daher, dass ich den Umgang mit Sympy vereinfachen will. Sympy habe ich nur verwendet, weil es hilft, das Prinzip zu veranschaulichen. Ich baue eine EDSL zur Logikprogrammierung, mit einer eigenen Symbol-Klasse. Jeder Name einer logischen Variablen, Konstanten oder eines Prädikats ist dabei ein Symbol. Da kommen schnell mal zig Namen zusammen. Deswegen habe ich einen Weg gesucht, das zu verkürzen. Mal sehen, wie es sich bewährt.
In specifications, Murphy's Law supersedes Ohm's.
Antworten