Przekazanie słownika do funkcji

Odpowiedz Nowy wątek
2011-08-23 19:19
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?

Pozostało 580 znaków

2011-08-23 19:24
Kumashiro
4

Normalnie. Przez referencję.

Minus za lakoniczność. Jak wygląda referencja w Pythonie? - vpiotr 2011-08-23 20:17
A jak ma wyglądać? Jak zmienna. Zmienne typów złożonych są referencjami, zatem słowniki przekazuje się normalnie, jako argument funkcji. Mam napisać jak się wywołuje funkcję z argumentem? - Kumashiro 2011-08-23 20:30
Plus bo 1) minus był bez sensu 2) bo odpowiedź jest na poziomie pytania 3) bo tak. - msm 2011-08-23 21:01
@MSM. Uważasz, że to pytanie jest banalne? Dla mnie nie. - vpiotr 2011-08-23 22:04

Pozostało 580 znaków

2011-08-23 20:14
0

Tu masz odpowiedź:

http://dev.fyicenter.com/Inte[...]n_with_output_parameters.html

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

Pozostało 580 znaków

2011-08-23 20:28
Kumashiro
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.

No właśnie - szerzysz dezinformację. Nie znam dobrze Pythona, dopiero się go uczę, ale z tego co wiem referencji jako składni (w sensie C++) w Pythonie nie ma - bo wszystkie struktury na wejściu są referencjami. Coś mi się jednak świta, że w jakimś celu przekazuje się argumenty z dwoma znakami z przodu - chyba "@@", ale nie mogę znaleźć przykładu ani opisu po co? - vpiotr 2011-08-23 20:37
Źle Ci świta. Pojedynczym znakiem '@' z przodu oznacza się dekoratory. Podwójny znak '@' można wstawić w miejscu, w ktrórym chcesz żeby wystąpił błąd składni (możesz też użyć jakiegoś innego znaku, np. '*'). Dezinformacji nie szerzę, gdyż wywołanie funkcji ze zmienną typu złożonego jako argumentem jest przekazaniem przez referencję. - Kumashiro 2011-08-23 20:46
Chyba to co mi świtało to kwargs, czyli coś zupełnie innego: http://programming.itags.org/python/59647/ - vpiotr 2011-08-23 20:49
"keyword arguments" tak, ale to jest co innego. - Kumashiro 2011-08-23 20:56
@vpiotr - jak na razie to ty szerzysz dezinformację i jeszcze coś ci 'świta'... :| - msm 2011-08-23 21:03

Pozostało 580 znaków

2011-08-23 20:47
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

Pozostało 580 znaków

2011-08-23 20:51
Kumashiro
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]
Tak, ale w przykładzie który podałem tę referencję "psują" nową wartością i wtedy już nie jest referencją. Czyli to taka pół-referencja. Nieładnie. - vpiotr 2011-08-23 21:17
Albo źle się wyraziłeś, albo nie rozumiesz czym jest referencja. - Kumashiro 2011-08-23 21:25
No w Pythonie to najwidoczniej nie rozumiem. Referencja = argument który jeśli zmienię wewnątrz funkcji, zmienia również zmienną podaną jako argument funkcji na zewnątrz. Jak widać w przykładzie z http://www.tutorialspoint.com w Pythonie nie zawsze tak jest. - vpiotr 2011-08-23 21:29
Nie. Referencja jest "etykietką". W Pythonie działa tak samo jak np. w C++ czy Javie. To nie jest "argument, który zmienia również zmienną". Nie wiem o którym przykładzie z tej strony mówisz, ale referencja w Pythonie zachowuje sie jak każda inna referencja. - Kumashiro 2011-08-23 21:35
W Javie nie ma referencji. - iooi 2011-08-24 14:27

Pozostało 580 znaków

2011-08-23 21:39
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"?

edytowany 1x, ostatnio: vpiotr, 2011-08-23 21:41

Pozostało 580 znaków

2011-08-23 21:49
Kumashiro
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!

Nie odpowiedziałeś na pytanie. Ten przykład dowodzi że C++ działa inaczej, a referencja w Pythonie jest raczej wskaźnikiem niż referencją w sensie C++. I dlatego lakoniczna odpowiedź nie była wystarczająca. Nawet wpisując w googla odpowiednie hasło ("python passing arguments by reference") niewiele można z tego się dowiedzieć. Owszem, Python pewnie używa tego co opisałeś jako "referencja" ale de facto jest to wskaźnik - tak jak w Delphi, C++, Java. Z tym, że na szczęście (?) ukryty. - vpiotr 2011-08-23 21:59
To nie jest wskaźnik. Nie rozumiesz co się dzieje w funkcji. Przekazujesz referencję w argumencie, a w ciele funkcji tworzysz nową referencję, przykrywając argument. Gdyby istniała możliwość dobrania się do argumentu, to nadal mógłbyś go zmieniać i działałby jak zwykła referencja. W C++ czegoś takiego nie zrobisz, bo kompilator nie zezwala na przykrycie argumentu. To, co Ty robisz to tworzysz nową zmienną (referencję) o takiej samej nazwie jak argument, ale nie niszczysz oryginału. Nie wiem już jak prościej Ci to wytłumaczyć. - Kumashiro 2011-08-23 22:09
Ależ owszem, C++ pozwala na przykrycie argumentu / struktury. Ale tylko jeśli jest przekazywana jako wartość (patrz iteratory) albo jako wskaźnik. Nigdy przez referencję. Nie musisz mi tego tłumaczyć, widzę przecież jako to działa, chodzi mi tylko o nazewnictwo. - vpiotr 2011-08-23 22:18
No cóż, mój kompilator (g++) nie zezwala ("error: declaration of 'int i' shadows a parameter"), niezależnie od tego czy argument jest przekazywany przez wskaźnik, referencję czy wartość. - Kumashiro 2011-08-23 22:27
Zmień na nowszy model :) A tak serio: void shadowBegin(std::vector<int>::iterator itBegin, std::vector<int>::iterator itEnd) { while(itBegin++ != itEnd) {...} } - wartość itBegin (klasa) zmieniana jest tylko wewnątrz funkcji... - vpiotr 2011-08-23 22:33
Ale w tej funkcji nigdzie nie przykrywasz itBegin, tylko wywołujesz na nim operator autoinkrementacji. Jakbyś w funkcji miał 'std::string itBegin;', to wtedy byś przykrywał. Chyba, że ja tu czegoś nie widzę (daj kod w nowym wpisie, bo od czytania komentarzy mi się już w oczach troi)... - Kumashiro 2011-08-23 22:42

Pozostało 580 znaków

2011-08-23 22:53
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
  }
}
edytowany 2x, ostatnio: vpiotr, 2011-08-23 22:56

Pozostało 580 znaków

2011-08-23 23:05
Kumashiro
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.

OK, dla mnie jest to jeden wniosek: referencja w Pythonie = wskaźnik w innych językach - vpiotr 2011-08-23 23:23
Echhh... No właśnie nie jest jak wskaźnik. Gdyby był, to do referencji mógłbyś przypisać inną referencję, a tak się nie da (tak, jak i w przypadku referencji w C++). Każda operacja przypisania typu złożonego tworzy nową referencję. Referencja w Pythonie jest jak referencja w C++. Albo inaczej, referencja jest etykietką, a nie zmienną przypisaną do wirtualnego obszaru pamięci, której zawartość możesz zmieniać. Może tak załapiesz o co mi chodzi. - Kumashiro 2011-08-23 23:33
Wiem o co chodzi, ja to widzę jako wskaźnik do struktury (jego zmiana zmienia wskazanie a nie zawartość obiektu wskazywanego), ale nie będę nikogo na to pojęcie nawracał. To chyba kwestia punktu widzenia. - vpiotr 2011-08-23 23:48
OK, najwyraźniej nie potrafię tego wytłumaczyć, więc ja już może zakończę. Źle to widzisz. Obawiam się też, że nie rozumiesz czym naprawdę jest referencja (w jakimkolwiek języku), albo najzwyczajniej w świecie nie rozumiem Twojego toku myślenia (albo i jedno i drugie). Proponuję EOT, bo nasze wywody zaczynają się robić nudne i krążyć w kółko. Tak czy siak, powodzenia w nauce Pythona. To bardzo przyjemny język. - Kumashiro 2011-08-23 23:56

Pozostało 580 znaków

2011-08-24 14:33
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.

edytowany 13x, ostatnio: iooi, 2011-08-24 15:13

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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