Przekazanie słownika do funkcji

0

Witam.
Mam w Python'ie coś takiego:

def funkcja1():
    d = {'Adam' : ['Monika', 'Kasia'], 'Mariusz' : ['Patrycja', 'Kasia']}

def funkcja2(zm):
    #tutaj chce przekazać słownik

Jak przekazać ten słownik z jednej funkcji do 2-giej?

4

Normalnie. Przez referencję.

0

Tu masz odpowiedź:

http://dev.fyicenter.com/Interview-Questions/Python/How_do_I_write_a_function_with_output_parameters.html

Z tego co wiem, to w Pythonie zalecany jest sposób pierwszy.

1
vpiotr napisał(a)

Z tego co wiem, to w Pythonie zalecany jest sposób pierwszy.

Słownik jest typem złożonym. W Pythonie wszystkie zmienne typów złożonych są referencjami. Zwracanie typu złożonego, przekazanego w argumencie (jak pokazuje sposób pierwszy) nie ma większego sensu. To rozwiązanie stosuje się w przypadku typów prostych, które są kopiowane w funkcjach:

#!/usr/bin/env python                                                                                                                                                                        

def funA(somedict):                                                                                                                                                                          
        print('Dictionary in funA() before change: ' + str(somedict))
        somedict['a'] = 1
        print('Dictionary in funA() after change: ' + str(somedict))


def funB():
        mydict = {'b': 2}
        print('Dictionary in funB() before call: ' + str(mydict))
        funA(mydict)
        print('Dictionary in funB() after call: ' + str(mydict))


def funC(someint):
        print('Integer in funC() before change: ' + str(someint))
        someint += 1
        print('Integer in funC() after change: ' + str(someint))


def funD():
        myint = 1
        print('Integer in funD() before call: ' + str(myint))
        funC(myint)
        print('Integer in funD() after call: ' + str(myint))

funB()
funD()

Wynik:

Dictionary in funB() before call: {'b': 2}
Dictionary in funA() before change: {'b': 2}
Dictionary in funA() after change: {'a': 1, 'b': 2}
Dictionary in funB() after call: {'a': 1, 'b': 2}
Integer in funD() before call: 1
Integer in funC() before change: 1
Integer in funC() after change: 2
Integer in funD() after call: 1

Z podanego przez Ciebie linka to nr 3 i 4 (które sa tym samym - przekazaniem przez referencję) jest zalecany w przypadku słowników.

0

OMG, to jest chyba bardziej skomplikowane:

  • argumenty proste są przekazywane przez wartość
  • struktury przez referencję
  • ale listy przez wartość?

http://www.tutorialspoint.com/python/python_functions.htm

1

Nie, lista też jest referencją:

#!/usr/bin/env python

def funA(somelist):
        print('List in funA() before change: ' + str(somelist))
        somelist.append(1)
        print('List in funA() after change: ' + str(somelist))


def funB():
        mylist = [2, 3]
        print('List in funB() before call: ' + str(mylist))
        funA(mylist)
        print('List in funB() after call: ' + str(mylist))


funB()

Wynik:

List in funB() before call: [2, 3]
List in funA() before change: [2, 3]
List in funA() after change: [2, 3, 1]
List in funB() after call: [2, 3, 1]
0

Piszę o tym:

# Function definition is here
def changeme( mylist ):
   "This changes a passed list into this function"
   mylist = [1,2,3,4]; # This would assig new reference in mylist
   print "Values inside the function: ", mylist
   return

# Now you can call changeme function
mylist = [10,20,30];
changeme( mylist );
print "Values outside the function: ", mylist

W jakiej formie referencja w C++ zachowa się tak samo? Tzn nie zmieni się wartość "outside"?

0

W komentarzu masz napisane co się dzieje. Tworzysz nową referencję w funkcji.
Może to rozjaśni sytuację:

#!/usr/bin/env python

def funA(somelist):
        print('List in funA() before append(): ' + str(id(somelist)))
        somelist.append(1)
        print('List in funA() after append(): ' + str(id(somelist)))
        somelist = [4, 5, 6]
        print('List in funA() after assign: ' + str(id(somelist)))


def funB():
        mylist = [2, 3]
        print('List in funB() before call: ' + str(id(mylist)))
        funA(mylist)
        print('List in funB() after call: ' + str(id(mylist)))
        print('Contents: ' + str(mylist))


funB()

Wynik:

List in funB() before call: 34315944
List in funA() before append(): 34315944
List in funA() after append(): 34315944
List in funA() after assign: 34056312
List in funB() after call: 34315944
Contents: [2, 3, 1]

Pamiętaj, że Python jest językiem dynamicznie typowanym!

0

Przykrywam, tyle że tego dobrze nie widać, może być tak:

// przykrycie w C++ argumentu przesłanego przez wartość:
void f1(std::vector<int>::iterator itBegin, std::vector<int>::iterator itEnd)
{ 
  if (jestWtorek()) {
    itBegin = itEnd; // przykrycie - zmieniłem lokalną wartość argumentu przesłanego przez wartość, 
   // *itBegin wskazuje na inny element niż na starcie
   // wywołania na itBegin odwołują się teraz do innego elementu
 }

  // cos tam robie z *itBegin
}

// przykrycie w C++ argumentu przesłanego przez wskaźnik:
void f2(char *cBeginPtr, char *cEndPtr)
{ 
   if(jestWtorek()) {
     cBeginPtr = cEndPtr; // przykrycie - zmieniłem lokalną wartość argumentu przesłanego przez wskaźnik, 
     // *cBeginPtr wskazuje teraz na inne miejsce w pamięci
     // zmiana *cBeginPtr nie zmieni zmiennej która była wskazana na początku o ile cBeginPtgr było różne od cEndPtr
  }
}

// referencja - brak przykrycia 
void f3(char &ptr1, char &ptr2)
{ 
   if(jestWtorek()) {
     ptr1 = ptr2; // zmieniłem zawartość zmiennej przekazanej do ptr1, a nie samo na nią wskazanie
  }
}
0

Jak już napisałem wcześniej, nie rozumiesz co się dzieje w funkcji.
OK, to korzystając z okazji, że wyskoczyliśmy z komentarzy, odpowiednikiem w języku C++ tego, co Ty robisz w funkcji Pythonowej jest taki kod:

void  funA(int  i)
{
    int i = 2;
}


int main(void)
{
    int     myint = 1;

    funA(myint);
    return 0;
}

O takie przykrycie mi chodzi. To niestety się nie skompiluje, ale obrazuje o co chodzi z tworzeniem nowej referencji w funkcji Pythonowej. W Pythonie, w funkcji tworzysz nową referencję, która ma taką samą nazwę jak argument. Argument zostaje przykryty (ale dalej sobie istnieje), zaś w funkcji pojawia się nowa zmienna lokalna, do której przypisujesz nową referencję... Mam nadzieję, że teraz zrozumiesz czego nie rozumiesz.

0

To jest wskaźnik, tak samo jak w Javie. Tylko zapis jest inny.

void modifyPtr(Foo* foo)
{
    foo = new Foo;
}

void modifyFoo(Foo* foo)
{
    foo->bar = baz;
}

Foo* foo = new Foo;
modifyPtr(foo); // niezmieniony
modifyFoo(foo); // zmieniony

jest równoznaczne z Javowym (i, jak widać, Pythonowym):

void modifyPtr(Foo foo) {
    foo = new Foo();
}

void modifyFoo(Foo foo) {
    foo.bar = baz;
}

Foo foo = new Foo();
modifyPtr(foo); // niezmieniony
modifyFoo(foo); // zmieniony

Dla referencji nie ma odpowiednika, nie można też wyłuskać wskaźnika (więc nie można przypisać innego obiektu do danego adresu).

void modify(Foo& foo)
{
    foo = Foo();
}

void modify(Foo* foo)
{
    *foo = Foo();
}

Obydwie funkcje zmieniają wartość pod adresem wskaźnika (wywołują operator przypisania), a nie samą wartość wskaźnika - co jest niemożliwe w Javie z w/w powodów.
Problemem jest to, że co innego nazywa się referencją w Javie (Pythonie), a co innego w C++. Po prostu źle dobrany termin. W każdym razie nie jest to pass by ref.
I ofc nie ma też przekazywania przez wartość obiektów.

0
iooi napisał(a)

To jest wskaźnik, tak samo jak w Javie. Tylko zapis jest inny.

W jednym z listingów wyżej udowodniłem że to nie jest "wskaźnik".

iooi napisał(a)

Dla referencji nie ma odpowiednika, nie można też wyłuskać wskaźnika (więc nie można przypisać innego obiektu do danego adresu).

Tak samo w Pythonie nie można przypisać nowej referencji do już istniejącej... Popatrz na listingi, które były wcześniej.

iooi napisał(a)

W każdym razie nie jest to pass by ref.
I ofc nie ma też przekazywania przez wartość obiektów.

Te dwa zdania są sprzeczne. Skoro nie jest to przekazanie przez referencję, obiektów nie można przekazać przez wartość, a w Pythonie nie ma wskaźników, to z Twojej wypowiedzi wynika, że nie ma możliwości przekazania obiektu do funkcji.

0

Udowodniłeś, dając przykład przekazywania przez wartość. Odpowiednikiem twojego:

void funA(int i)
{
    int i = 2; // chodziło pewnie o i = 2;
}

Byłoby, wykorzystując foologię:

void funA(Foo foo)
{
  foo = Foo();
}

Czego ani w Javie, ani w Pythonie zrobić się nie da (w Javie np. obiektu nie da się przekazać w ogóle - zawsze będzie to wskaźnik).

Przy okazji znalazłem ciekawy materiał:
http://javadude.com/articles/passbyvalue.htm

Tutaj fragment z JLS:

The reference values (often just references) are pointers to these objects, and a special null reference, which refers to no object.

0
iooi napisał(a)

Udowodniłeś, dając przykład przekazywania przez wartość.

Nie. Dowód masz tutaj (powtórzę):

#!/usr/bin/env python
 
def funA(somelist):
        print('List in funA() before append(): ' + str(id(somelist)))
        somelist.append(1)
        print('List in funA() after append(): ' + str(id(somelist)))
        somelist = [4, 5, 6]
        print('List in funA() after assign: ' + str(id(somelist)))
 
 
def funB():
        mylist = [2, 3]
        print('List in funB() before call: ' + str(id(mylist)))
        funA(mylist)
        print('List in funB() after call: ' + str(id(mylist)))
        print('Contents: ' + str(mylist))
 
 
funB()
iooi napisał(a)

Odpowiednikiem twojego:

void funA(int i)
{
    int i = 2; // chodziło pewnie o i = 2;
}

Nie, to nie jest dowód. To było zobrazowanie tego, co miałem na mysli pisząc o przykrywaniu. I nie chodziło o i = 2, lecz dokładnie o to, co tam jest napisane.
Bardzo Cię proszę o przeczytanie uważnie całej dyskusji i dopiero po tym zabieranie głosu.

0

Ale to żaden obraz, to tylko compile-error.
A ten dowód właśnie pokazuje, że do funkcji nie była przekazana referencja (znana z C++), tylko zwykły wskaźnik. Chyba, że nie wiesz, jak działają referencje w C++.

1

Panowie, wszyscy się mylicie. @iooi, proszę, nie mieszaj do tego Javy, która nie jest językiem w pełni obiektowym, w przeciwieństwie do Pythona. Mówienie o wskaźnikach nie ma tutaj najmniejszego sensu.

Kumashiro napisał(a)

Słownik jest typem złożonym. W Pythonie wszystkie zmienne typów złożonych są referencjami. Zwracanie typu złożonego, przekazanego w argumencie (jak pokazuje sposób pierwszy) nie ma większego sensu. To rozwiązanie stosuje się w przypadku typów prostych, które są kopiowane w funkcjach

Python nie posiada rozróżnienia na value types i reference types, tutaj nie ma trzymania, bądź przekazywania, przez wartość, absolutnie wszystko jest na swój sposób "referencyjne". Sytuacja jest identyczna jak w LISPie, pewne instancje są widziane pod pewnymi symbolami, nie są z nimi fizycznie związane. W Pythonie nie ma niejawnego kopiowania obiektów, ew. kopiowanie zawsze należy przeprowadzić ręcznie.

Zacznijmy od podstaw, tj. "wszystko jest obiektem" (do tego pierwszej kategorii). "Typy proste" z Javy czy C++ w Pythonie są pełnoprawnymi obiektami, chociaż nie posiadają wewnętrznej wartości, z punktu widzenia użytkownika są niezmienne. Funkcje i metody są obiektami pierwszej kategorii, możemy więc posługiwać się nimi na równi z innymi instancjami:

# postać bezpośrednia
obiekt.metoda(69)
# równoważna konstrukcja wynikajaca z powyższej własności:
zrobCoś = obiekt.metoda
zróbCoś(666)

Powiedzmy, że chcemy stworzyć własne funkcje do inkrementacji, dekrementacji i negacji liczb, większość ludzi mających mylne pojęcie o Pythonie zrobi to tak (lub posługując się lambdami):

def inc(x):
    return +1 + x

def dec(x):
    return -1 + x

def neg(x):
    return -1 * x

Jest to podejście wywodzące się z przeświadczenia, że licza jest wartością, tak jak w prymitywnym C++, czy nieudolnie zaprojektowanej Javie. Jak już wspomniałem, w Pythonie wszystko jest obiektem, do tego metody są obiektami pierwszej kategorii, stąd też możemy powyższe zapisać w sposób następujący:

inc = (+1).__add__
dec = (-1).__add__
neg = (-1).__mul__

"Wartościami" inc, dec i neg są metody zbindowane z obiektami, na rzecz których zostaną wywołane (czy też właściwie, z których pochodzą) czyli lewą stroną operatora, dopiero czekają na argument (tj. prawą stronę operatora). Przykład może trochę naciągany, ale jego celem jest demonstracja mechaniki.

No dobrze, skoro liczby są obiektami, to dlaczego można wykonywać na argumencie funkcji operacje np. += i nie zmienić zewnętrznej zmiennej? Odpowiedź jest prosta: typy 'niezmienne' (liczby, krotki frozensety etc.) nie posiadają operatorów skróconego przypisania, tj. __iadd__ etc. Operacje skrócone realizowane są w następujący sposób: jeżeli obiekt posiada metodę realizującą skróconą operację to zostanie ona wykonana, w przeciwnym wypadku zostanie użyta operacja podstawowa w pełnym wymiarze. Jeżeli obiekt oferuje modyfikacje in-place to zostają one wykonane, chociaż to na metodzie spoczywa odpowiedzialność za zwrócenie "referencji" do obiektu, który jest wynikiem takiej operacji, także nawet modyfikacja in-place może dać na wyjściu inny obiekt, inną wartość zmiennej, chociaż nie jest to zalecane. Przykładowa sesja z IPythona potwierdzająca akapit:

In [1]: hasattr(0, '__add__')
Out[1]: True

In [2]: hasattr(0, '__iadd__')
Out[2]: False

In [3]: class UdajemyInta(object):
   ...:     def __init__(self, value):
   ...:         self.value = value
   ...:     def __add__(self, other):
   ...:         return UdajemyInta(self.value + other.value)
   ...:     def __str__(self):
   ...:         return str(self.value)
   ...:     def __repr__(self):
   ...:         return repr(self.value)
   ...:

In [4]: def fun(x):
   ...:     x0 = x
   ...:     x += x
   ...:     print('Czy ten sam? %s. Wartosc: %s.' % (x0 is x, x))
   ...:

In [5]: lista = [69, 666]

In [6]: fun(lista)
Czy ten sam? True. Wartosc: [69, 666, 69, 666].

In [7]: udawanyInt = UdajemyInta(666)

In [8]: fun(udawanyInt)
Czy ten sam? False. Wartosc: 1332.

Na koniec możemy jeszcze sprawdzić wartości naszych dwóch zmiennych (lista i udawanyInt) po wyjściu z funkcji wykonującej +=:

In [9]: lista
Out[9]: [69, 666, 69, 666]

In [10]: udawanyInt
Out[10]: 666

int nie posiada operatora +=, tak samo jak nasz ewidentnie "złożony" typ, mimo wszystko kod się wykonuje, co więcej wyniki dla "udawanego inta" są takie, jakie powinny być dla "typu prostego". No i co, panowie? W Pythonie WSZYSTKO jest "referencją" (chociaż wewnętrznie dokonywane są optymalizacje reprezentacji), z typów "prostych" (poza boolean i NoneType) można nawet dziedziczyć.

Cholera, zostawić Was na dwa dni to nowego Pythona wynajdziecie...

0

Racja. Moje postrzeganie było upośledzone. No cóż, człowiek uczy się całe życie :)
Ale i tak moja teoria była zabawniejsza...

0

Mieszam w to Javę, bo była tu wywoływana i jest C-like, więc można było łatwiej odwołać się do (również wywołanego) C++.
To, co pokazałeś, nie jest żadnym specjalnym zachowaniem i nie wiem za bardzo, jak to się ma do naszej dyskusji. Jeśli nie Java, to odpowiednik w Scali (tak, tej Scali, która działa na JVM!), którego działanie jest identyczne:

case class UdajemyInta(value: Int) {
  def +(other: UdajemyInta) = UdajemyInta(value + other.value)
  override def toString = value.toString
}

def fun(x: ListBuffer[Int]) {
  var x0 = x
  var x1 = x
  x1 ++= x  // nieznaczaca roznica
  printf("Czy ten sam? %s. Wartosc: %s.%n", x0 eq x1, x1)
}

// Scala nie jest dynamicznie typowana
def fun(x: UdajemyInta) {
  var x0 = x
  var x1 = x
  x1 += x
  printf("Czy ten sam? %s. Wartosc: %s.%n", x0 eq x1, x1)
}

val lista = ListBuffer(69, 666)
fun(lista)
// Czy ten sam? true. Wartosc: ListBuffer(69, 666, 69, 666).

val udawanyInt = UdajemyInta(666)
fun(udawanyInt)
// Czy ten sam? false. Wartosc: 1332.

Oczywiście, że mówienie o wskaźnikach nie ma tu sensu, ale tylko dlatego, że to zbyt wysoki poziom abstrakcji - jednak w każdym razie, żaden z tych języków nie posiada referencji w stylu C++.

0
iooi napisał(a)

Mieszam w to Javę, bo była tu wywoływana i jest C-like, więc można było łatwiej odwołać się do (również wywołanego) C++.
To, co pokazałeś, nie jest niczym specjalnym i nie wiem za bardzo, jak to się ma do naszej dyskusji. Jeśli nie Java, to odpowiednik w Scali (tak, tej Scali, która działa na JVM!), którego działanie jest identyczne

Doskonale wiem, że w Scali działa to identycznie jak w Pythonie. Jak to się ma do dyskusji? Wątek nie jest o Javie czy C/C++ tylko o Pythonie, do tego siejecie dezinformację nt. przekazywania przez wartość. Jako osoba znająca Scalę już dawno powinieneś zastrzec, że immutable nie oznacza by-value.

0

Nikt nie twierdził, że cokolwiek jest tu przekazywane przez wartość. Ja starałem się tylko sprostować, że nie jest przez referencję ("Nie. Referencja jest "etykietką". W Pythonie działa tak samo jak np. w C++ czy Javie.").

0
iooi napisał(a)

Nikt nie twierdził, że cokolwiek jest tu przekazywane przez wartość. Ja starałem się tylko sprostować, że nie jest przez referencję ("Nie. Referencja jest "etykietką". W Pythonie działa tak samo jak np. w C++ czy Javie.").

Za to sobie cenię Delphi czy Jave (której zresztą poza tym nie znam), że obiekty zawsze przekazywane są w ten sam sposób - wskaźnikiem. A nie referencją, wskaźnikiem, przez wartość czy przez smart pointer...
Wbrew pozorom to minimalizowało wycieki, bo wiadomo było, że jak się jakiś obiekt tworzy to trzeba go zwolnić i kropka.

1 użytkowników online, w tym zalogowanych: 0, gości: 1