[megoldva] Bash "source"-nak megfelelő környezet-követő Python futtatás interaktív parancssorból

Egy ahhoz hasonló környezetre vágyom, mint ami a Google Colab-ban is van, Python3-ra – vagy ahogy a bash is intézi a source-olást (. script).

Leginkább az kellene, hogy amikor lefuttatok két szkriptet, az elsőnek a változóit, eredményeit ne felejtse el a parancssor, amikor indítom a másikat. ( https://realpython.com/run-python-scripts/ – ebben utánanéztem, hogy az "import" effélét csinál, de mégis, mikor lefuttattam (importtal) az első részt, és indítottam volna a másodikat, NameError: name 'valami' is not defined jött. Tehát elfelejtett mindent, amit az első futamban csinált.

Ennek az egésznek az a motivációja, hogy az első rész sokáig fut, a másodikat viszont többször szükséges lehet újraindítani (és ez utóbbi nem fut sokáig, de támaszkodik az első eredményeire).

Hozzászólások

Szia,

én csinálnék egy osztályt, ahol az első szkript és a második szkript külön függvényként szerepelnének és első modul futtatása után a kimenetet változóba mentve a második bármikor elérheti. Nem tudom mennyire fedi ez így le a use case-t.

Bár már mondták a többiek, de ha ilyesmi módon akarod használni, akkor neked valami interaktív shell szerű kell. Vannak az ilyen nagyon fancyk, mint Jupiter (sose használtam, de elvileg ilyesmikre jó), vagy egy sima repl. Maga a python is megy, csak kissé fapados, én ptpython-t szoktam használni, az egész fancy. Aztán ott lehet tolni, hogy

import script1
import script2

result = script1.run()
script2.run(result)
# baszki
script2.run(result)
script2.run(result)
script2.run(result)

Ha nem akarsz átadással bohóckodni, akkor a scripteket talán meg lehet úgy tákolni, hogy bele tudjon polluteolni az interpreter namespace-ébe, de az valami csúnyaság lesz.

Illetve azt még azért hadd jegyezzem meg, hogy elég humoros, amikor 'szar a python' fennhanggal kéred számon egy programnyelvtől, hogy belül nem működik interaktív shell szerűen. Ráadásul nem veszed észre, hogy a shellben így átadni max lapos listákat meg hasheket lehet, azért érzed, hogy az kicsit más.

Ps: van xonsh is, lehet, hogy az még handybb ilyen usecasekre, én még nem mertem kipróbálni, mert a való élethez sajtreszelőnek tűnt

Igen, elismerem, hogy volt egy ilyen kicsengése a gondolatmenetemnek, hogy 'szar a python', de igazából ezt csak bosszankodásomban írtam.

Ha jobban belegondolok, igazából a régi vágású gondolkodásomban van a hiba, vagyis a nem-eléggé-objektumorientált szkriptelő stílusomban.

A pythont nagyon sokra tartom, még akkor is, ha nehezen áll rá a fejem.

Azt azért továbbra is fenntartom, hogy jólesne egy olyan python utasítás, ami egyenértékű azzal, hogy "interaktív shellbe (editorból kimásolás után) beragasztok egy kódrészletet és hadd fusson, utána pedig ne feledd az eredményeket". Azaz, mint ami a bash-ben a source.

Ha sourceval 'behuzol' egy filet, akkor az aktaulisan futo ertelmezo (azaz, az a shell,amiben eppen pont ott te vagy, fuggetlenul attol, hogy mi szerepel a shebangnal) fogja ertelmezi azt az allomanyt, felteve persze, ha van benne source parancs. Ha ez a shell kompatibilis azzal a 'nyelvvel', amit a behuzott allomanyban hasznalsz, akkor le fog futni benne a dolog. De itt van peldaul a csh shell, ami ugyan ismeri a source parancsot, viszont nem erti az sh szintaxist, igy hiaba source-olsz ott be egy mas nyelvben irt scriptet, az nem fog menni.

Értem, amit írsz, de itt nyelvek keveréséről nem volt szó az én értelmezésemben. Tehát nem akartam bash-ből pythont értelmezni vagy csh-ból sh-t. Amire vágytam, az egy bármilyen-python-szerű shell, amiből több python szkriptet tudok source-olni (vagy futtatni) egymás után, s aminél nem kell bíbelődni a változók átadásával, szerializálásával.

Kicsit konkrétabb kellene. Konkrétan az environment változókat szeretnéd, vagy valami beazonosítható eredményeket, vagy mit? Illetve milyen viszonyban van az első, meg a második?

Mert mintha azt szeretnéd, hogy az első lefutásának eredményét tudd sourceolni, de ilyet a bash se csinál, sourcenál kapsz új "példányt", újra fog kezdődni az élet. Az alapján amit írsz, én azt mondanám, hogy a sokáig futó szépen serializálja az eredményeit valahova.

import json

... számol ...
json.dump(első_eredmény) # kiírni fájlba

- * -

import json

első_eredmény = json.load(....) # be a fájlból
... tovább számol ...
 

Amit szeretnel, az teljesen logikatlan es ertelmetlen. Megpedig azert, mert kevered a dolgokat. Esetedben, amikor bash shellben vagy, akkor eppen egy interaktiv modon futo basht hasznalsz, ebben tudsz masik bash scriptet behivni (source/.). A python ennek megfelelo "analogiaja" az, ha elinditassz egy interaktiv pythont (amit vetlelenul, egyebkent itt is python shellnek hivnak), majd ebben hivod be a kesz scriptjeidet (import).

A megoldas jelen esetben az, ha az elso script eredmenyet valamilyen formaban elmented (pickle/json), majd ezt hasznalod egy masik allomanybol.

Lehet, hogy te logikátlannak és értelmetlennek látod, de számomra van benne ráció. :-)

Elfogadom, amit írtál, hogy az analógia sántít a bash shellel.

Az is valószínű, hogy a lustaság (is) beszél belőlem (illetve a szerializációtól való ódzkodás, ha nem muszáj).

A helyzet a következő módon alakult ki:

 - adott egy naaagy python szkript. Fut, fut.
 - az első része jó sokáig.
 - utána jön a második része, és pikk-pakk lehal.

Gondoltam, nosza, vágjuk ketté, és ami sokáig fut, azt ne kelljen újra futtatgatni.

Hiszen ilyet bashben gyerekjáték... csak olló kell hozzá.

De lehet, hogy naivitás volt részemről, hogy pusztán ollóval akartam ezt megoldani.

Szia! Ha jól értem akkor neked igazából pont arra van szükséged ahogyan a Google Colab (vagy bármilyen jupyter notebook) esetén szervezni szokás a programokat:

- a hosszan futó meg a röviden futó részedet tedd egy-egy modulba (=függvényként fájlba)

- indíts egy interaktív python shellt

- import-old be a két modulodat

- az első hosszan futó függvényt hívd meg és tárold el a futási eredményt egy változóba

- ezután a második függvényt akárhányszor hivogathatod ebből az interaktív shellből

opcionális kanyar: ha a második függvény helyben módosítja az első eredményeként kapott adatokat, akkor opcionálisan érdemes lehet lemásolni azokat mielőtt hívod a második függvényt (ez a funkcionalitás lehet a második függvény elején is)

példakód:

#elso.py tartalma:
import time

def elso():
  # hosszan futok
  time.sleep(10)
  return ['ezt','a','tombot','allitom','elo']
# masodik.py tartalma:
def masodik(elsobol):
  # elsobol = az eredmeny az elozo lepesbol
  print(elsobol[1])
# python interaktiv shellbe:
import elso
import masodik

eredmeny = elso.elso()

# ezt akarhanyszor futtathatod
masodik.masodik(eredmeny)
masodik.masodik(eredmeny)
masodik.masodik(eredmeny)
masodik.masodik(eredmeny)
masodik.masodik(eredmeny)

Nem tudok róla.  De minden változót, ami fontos, betehetsz egy listába. Az alábbi példában a betöltést ugyanabbana sorrendben kell elvégezni, mint a dump-ot.

    # első.py
    import pickle                  

    l=[1,2,3]                      
    d={"egy":1}                    

    with open("rslt","wb") as ff:
        pickle.dump(l,ff)
        pickle.dump(d,ff)

    # második.py
    import pickle                  

    with open("rslt","rb") as ff:
        ll= pickle.load(ff)
        dd= pickle.load(ff)

Őszintén szólva most nem tudom megmondani; valamikor tudtam😀

A pickle "natívabb", ezt használja az értelmező a processzek és threadek közti adatcserénél is csak nem  fájlba dumpolva, hanem sztringbe (dumps). Talán összetettebb objektumokat lehet vele kezelni. (A fenti példámban  a lista túl egyszerűre sikeredett.)

Tetszik. Az adat bson-jellegű (binary json).
Egy dologgal viszont kapásból többet tud, "multi-json"-ként viselkedik. Ha úgy tetszik, van egy fájlon/streamen belüli "json elválasztó" delimitere (0x2e) is. Itt egy szemléletes példa:
 

#!/usr/bin/python3

import json
import pickle

l=[1,2,3]
d={"egy":1}

with open("rslt","wb") as ff:
    pickle.dump(l,ff)
    pickle.dump(d,ff)

# kézzel betett határoló jelölés kell a json-ok közé, ez esetben a sorvég az
with open("teszt_j1.json","w") as ff:
    json.dump(l, ff)
    ff.write("\n")
    json.dump(d, ff)

# vagy egyetlen tuple-ba rendezve, egyetlen json legyen
with open("teszt_j2.json","w") as ff:
    json.dump( (l, d), ff)

# ---------------------
# - másik python fájl -
# ---------------------

#!/usr/bin/python3

import json
import pickle


with open("rslt","rb") as ff:
    ll = pickle.load(ff)
    dd = pickle.load(ff)

# egyesével dolgozzuk fel a fenti, kézzel berakott határoló jelölés mentén (itt sorvégjel)
with open("teszt_j1.json","r") as ff:
    llj1 = json.loads(ff.readline())
    ddj1 = json.loads(ff.readline())

# vagy az egyetlen tuple-ba pakoltként küldöttet szedjük szét a nevesített változókra
with open("teszt_j2.json","r") as ff:
    (llj2, ddj2) = json.load(ff)

print (ll, "----", dd)
print (llj1, "----", ddj1)
print (llj2, "----", ddj2)

Rust modul is támogatja: https://crates.io/crates/serde-pickle, ezáltal átadható közöttük is ilyen formátumban az adat.
Eddig mindenhol formázott vagy egysoros json-okból álló adathalmazt használtam. De mostantól ezt is megjegyzem. Köszi mégegyszer.

A korábbi példám helyett ez izgalmasabb:

In [1]: d= {"A":1, (3,None): 2.5}                                                                                             

In [2]: l=[ 1, "Helló", d ]                                                                                                   

In [3]: import pickle                                                                                                         

In [4]: with open("rslt","wb") as ff: 
   ...:     pickle.dump(l,ff) 

A fentinél elakad a json.dump, nem tetszik neki, hogy a szótárban tuple az egyik kulcs.

Jó tudni a rust-os kapcsolatról. A korábbi, a rust-ról szóló bejegyzéseid már felkeltették az érdeklődésemet:-)

Hmm. Nem tudom hülyeségnek tartani, de esküszöm, hogy kulcsként még nem jutott eszembe a tuple.
Jönnek a különbségek. Vajon tuple mely nyelven lehet kulcs

Python ... képlékenyen átalakulnak a típusok:
>>> d= {"A":1, (3,None): 2.5}
>>> d[3,None]
2.5
>>> d.get((3,None))
2.5
>>> d.get((3,True))
>>>

Rust a merev típusossága (azaz nem képlékenyen átformálódó) miatt sokkal keményebben játsza a csiki-csukit, de rábeszélhető.
https://play.rust-lang.org/?version=stable&mode=release&edition=2018&gi…

Nem tudom hülyeségnek tartani, de esküszöm, hogy kulcsként még nem jutott eszembe a tuple.

Neha kenyelmes, kb. ugyanarra, amire a ritka matrix egy ertelmes koncepcio. Sokszor van 2 dimenzio->1 dimenzio osszerendeles, ahol az elemek donto tobbsege ertelmetlen, vagy valami default ertek. Ilyenkor egy {(i,j):val} ertekekbol allo dictionary jol johet. (es ebben nem csak szam lehet a scipy-os sparse matrixokkal ellentetben)

A strange game. The only winning move is not to play. How about a nice game of chess?

A JSON eleg jo, de korlatozott az abrazolasi kepessege. Van par primitiv (egesz vagy tizedestort szam, string), ezekbol epitheto lista meg dictionary. A pickle ennel bonyolultabb dolgokat is el tud tarolni, binaris (ennek minden elonyevel es hatranyaval), es a vegen ugyanaz az objektum esik ki belole. Pl. ha van egy tobbdimenzios numpy tombod, akkor JSON eseten ebbol elobb listak listajat kell csinalnod, mert a JSON nem ismeri a numpy tomb tipust. Aztan kerdes, hogy a - mondjuk 64 bites - floating point ertekedet hany tizedesre akarod kerekiteni, mert itt ugye veszitesz a pontossagabol. Aztan betolteskor ugyanez visszafele. Pickle eseten ugyanugy elteheted, mint barmi mast, esszeru korlatokkal (egy TCP/IP socketet hiaba csomagolnal be es tennel el, masik gepen masnap eloszedve mar csak szakadt kapcsolatot kapnal). Pandas dataframe-et is megalkotsz, elteszed (ott meg kulon wrappert is kapsz, hogy a 2 sor helyett 1 sor kelljen a pickle-be csomagolashoz), es utana hasznalhatod amikor csak kell. (hosszabb adattisztitashoz kenyelmes, ha a felkesz eredmenyt el tudod tenni)

Ha zavar, hogy binaris, base64-en keresztultolhato az eredmeny - bar kezzel nem nyulnek bele a JSONnel ellentetben, de pl. DB-be mentettem mar el igy objektumokat stringesitve. (lett volna mas lehetoseg is, elonyoket es hatranyokat figyelembe veve ez bizonyult itt a legjobbnak)

A strange game. The only winning move is not to play. How about a nice game of chess?

Szerintem nem akarsz mindent serializalni, mert vannak beepitettek is, amit jobb bekenhagyni. Mindenesetre van egy globals() es egy locals() fuggveny, ami visszaadja, hogy milyen globalis/lokalis valtozokat lat.

A strange game. The only winning move is not to play. How about a nice game of chess?

Szerkesztve: 2020. 09. 14., h – 16:28

Irsz egy blabla.py-t, amiben benne vannak a dolgaid. Ez lesz az, amit source-olsz. Utana:

from blabla import *

Innentol elersz mindent, amit blabla-ban letrehoztal. Hasznalhatod az "import blabla" parancsot is, akkor viszont blabla.akarmi neven tudod elerni amit abban a file-ban felvettel. (en utobbit jobban szeretem, ugy nem lesz nevutkozes)

Blabla termeszetesen lehet masik konyvtarban is:

from myrootdir.mydir.blabla import *

Esetleg importalod a sys-t, es utana a sys.path-ot beallitod, hogy hol keresse:

import sys
sys.path.append('/home/username/scripts/my_module')

from blabla import *

(a sys.path egy sima lista, szoval ugyanugy kezelheted, ahogy egy listat szokas)

ujabb szerk: Ez persze a cimben szereplo source-olos problemara ad megoldast. Hosszu futashoz valoban nem ez kell, hanem a pickle.

Azt egyebkent ugy szoktam hasznalni, hogy listaba/tuple-be teszem amit ki akarok menteni, es utana szinten listat/tuple-t olvasok belole:

class kkdb:
...
  def save(self):
    with open(self.filename, 'wb') as f:
      pickle.dump((self.servername,self.comp_id,self.races,self.competitors,self.startlist),f)
    
  def load(self):
    with open(self.filename, 'rb') as f:
      (self.servername,self.comp_id,self.races,self.competitors,self.startlist)=pickle.load(f)

A strange game. The only winning move is not to play. How about a nice game of chess?