|
269 | 269 | ارسال آرگومان |
270 | 270 | -------------- |
271 | 271 |
|
| 272 | +به صورت خودکار با ارسال آرگومان به تابع، متغیرهایی محلی از انتساب اشیای آرگومانها به اسامی پارامترهای موجود در سرآیند تابع به وجود میآیند:: |
| 273 | + |
| 274 | + >>> def f(a): |
| 275 | + ... print(a*a) |
| 276 | + ... |
| 277 | + >>> |
| 278 | + >>> b = 3 |
| 279 | + >>> f(b) |
| 280 | + 9 |
| 281 | + |
| 282 | +*با فراخوانی تابع f در نمونه کد بالا، متغیر محلی a ایجاد میگردد که به شی صحیح 3 اشاره دارد.* |
| 283 | + |
| 284 | +توجه داشته باشید که با انتساب شیایی جدید به پارامترهای تابع، عملا ارسال آرگومان بیتاثیر میگردد:: |
| 285 | + |
| 286 | + >>> def f(a): |
| 287 | + ... a = 2 |
| 288 | + ... print(a*a) |
| 289 | + ... |
| 290 | + >>> b = 3 |
| 291 | + >>> f(b) |
| 292 | + 4 |
| 293 | + |
| 294 | + |
| 295 | +**نکته مهم در ارسال آرگومان، توجه به چگونگی آن است!** |
| 296 | + |
| 297 | +در بین زبانهای برنامهنویسی دو شیوه برای ارسال آرگومان رایج است: **”by value“** و **”by reference“**. در شیوه by value یک کپی از مقدار آرگومان به تابع ارسال میگردد و در نتیجه با تغییر مقدار پارامتر متناظر در تابع، مقدار آرگومان ارسال شده در خارج از تابع بدون تغییر باقی میماند. به مثال پایتونی پایین توجه نمایید:: |
| 298 | + |
| 299 | + >>> def f(a): |
| 300 | + ... a = 2 |
| 301 | + ... print(a*a) |
| 302 | + ... |
| 303 | + >>> b = 3 |
| 304 | + >>> f(b) |
| 305 | + 4 |
| 306 | + >>> b |
| 307 | + 3 |
| 308 | + |
| 309 | +*همانطور که در نمونه کد بالا قابل مشاهده است، مقدار متغییر b بدون تغییر باقی مانده است.* |
| 310 | + |
| 311 | +ولی در شیوه by reference به جای ارسال یک کپی از مقدار آرگومان، یک ارجاع (reference) به آن آرگومان به تابع ارسال میگردد. میتوان اینطور در نظر گرفت که پارامتر متناظر در تابع، همان آرگومان در خارج از تابع است. در نتیجه با تغییر مقدار پارامتر متناظر در تابع، مقدار آرگومان در خارج از تابع نیز تغییر میکند. به مثال پایتونی پایین توجه نمایید:: |
| 312 | + |
| 313 | + >>> def f(a): |
| 314 | + ... a[0] = 3 |
| 315 | + ... print(a) |
| 316 | + ... |
| 317 | + >>> b = [1, 2] |
| 318 | + >>> f(b) |
| 319 | + [3, 2] |
| 320 | + >>> b |
| 321 | + [3, 2] |
| 322 | + |
| 323 | +این دو از شیوههای مرسوم در زبانهای برنامهنویسی هستند ولی ارسال پارامتر به صورت خاص در زبان برنامهنویسی پایتون چگونه است؟ در پایتون هر چیزی یک شی است و در نتیجه ارسال آرگومانها در هر شرایطی به صورت **”by reference“** انجام میپذیرد. |
| 324 | + |
| 325 | +و اگر سوال شود که علت تفاوت رفتار در دو مثال قبل چیست؟ باید بدانیم که علت به ماهیت اشیای آرگومانهای ارسالی مربوط است. ارسال اشیای تغییرناپذیر (Immutable) به مانند انواع بولین، اعداد، رشته و تاپل به تابع، باعث بروز رفتاری مشابه با شیوه by value میشود ولی در مورد ارسال اشیای تغییرپذیر (Mutable) به مانند انواع لیست، دیکشنری و مجموعه اینگونه نخواهد بود. به تصاویر پایین توجه نمایید: |
| 326 | + |
| 327 | +.. image:: /_static/l12-python-passing-arguments-01.png |
| 328 | + :align: center |
| 329 | + |
| 330 | +.. image:: /_static/l12-python-passing-arguments-02.png |
| 331 | + :align: center |
| 332 | + |
| 333 | +اشیای تغییرپذیر در پایتون اشیایی هستند که بدون تغییر ``()id`` آنها، مقدارشان قابل تغییر است. خروجی تابع ``()id`` برای هر شی بیانگر شناسه منحصر به فرد آن شی است که در واقع نشانی آن در حافظه نیز میباشد [`اسناد پایتون <http://docs.python.org/3/library/functions.html#id>`__] - درس پنجم. |
| 334 | + |
| 335 | +برای جلوگیری از تغییر اشیای تغییرپذیر درون تابع، میتوان به گونهایی که در درس هشتم گفته شد یک کپی از این نوع اشیا را ایجاد و سپس به صورت آرگومان به تابع ارسال کرد:: |
| 336 | + |
| 337 | + >>> def f(a): |
| 338 | + ... a[0] = 3 |
| 339 | + ... print(a) |
| 340 | + ... |
| 341 | + >>> b = [1, 2] |
| 342 | + >>> f(b[:]) # Pass a copy |
| 343 | + [3, 2] |
| 344 | + >>> b |
| 345 | + [1, 2] |
| 346 | + |
| 347 | +در نمونه کد بالا، از آنجایی که تمام اعضای شی لیست متغیر b تماما از انواع تغییرناپذیر هستند، یک کپی سطحی (Shallow Copy) از شی کفایت میکند ولی در حالتی غیر از این میبایست یک کپی عمیق (Deep Copy) از شی ارسال گردد - درس هشتم. |
| 348 | + |
| 349 | +البته گاهی واقعا نیاز است که مقدار تغییر یافته از متغیری که به تابع ارسال میشود را نیز بیرون از تابع هم در اختیار داشته باشیم. برای این منظور در برخی از زبانهای برنامهنویسی امکان ارسال به شیوه by reference بنابر خواست برنامهنویس فراهم شده است. برای مثال در زبان php این کار با قرار دادن یک ``&`` در پشت پارامتر مورد نظر انجام میپذیرد: |
| 350 | + |
| 351 | +.. code-block:: php |
| 352 | + |
| 353 | + <?php |
| 354 | + function foo(&$var) |
| 355 | + { |
| 356 | + $var++; |
| 357 | + } |
| 358 | +
|
| 359 | + $a=5; |
| 360 | + foo($a); |
| 361 | + // $a is 6 here |
| 362 | + ?> |
| 363 | +
|
| 364 | +در پایتون چنین قابلیتی وجود ندارد، حداقل برای اشیای تغییرناپذیر! ولی میتوان با استفاده از امکان بازگشت چندین شی توسط دستور ``return``، آن را پوشش داد. با استفاده از این شیوه میتوان هر تعداد از پارمترهای مورد نیاز خود را به خارج از تابع انتقال داد:: |
| 365 | + |
| 366 | + >>> def multiple(x, y): |
| 367 | + ... x = 2 |
| 368 | + ... y = [3, 4] |
| 369 | + ... return x, y |
| 370 | + ... |
| 371 | + >>> X = 1 |
| 372 | + >>> Y = [1, 2] |
| 373 | + >>> |
| 374 | + >>> X, Y = multiple(X, Y) |
| 375 | + >>> |
| 376 | + >>> X |
| 377 | + 2 |
| 378 | + >>> Y |
| 379 | + [3, 4] |
| 380 | + |
| 381 | +توجه داشته باشید که در این حالت دستور ``return`` تمام این اشیا را در قالب یک شی تاپل برمیگرداند:: |
| 382 | + |
| 383 | + >>> multiple(X, Y) |
| 384 | + (2, [3, 4]) |
| 385 | + |
| 386 | + |
| 387 | +تطابق آرگومانها |
| 388 | +------------------ |
| 389 | +پیشتر به لزوم همخوانی تعداد آرگومانهای ارسالی با پارامترهای موجود در سرآیند تابع اشاره شد:: |
| 390 | + |
| 391 | + >>> def f(a, b, c): |
| 392 | + ... pass |
| 393 | + ... |
| 394 | + >>> |
| 395 | + >>> f(1, 2) |
| 396 | + Traceback (most recent call last): |
| 397 | + File "<stdin>", line 1, in <module> |
| 398 | + TypeError: f() missing 1 required positional argument: 'c' |
| 399 | + >>> |
| 400 | + >>> f(1, 2, 3, 4) |
| 401 | + Traceback (most recent call last): |
| 402 | + File "<stdin>", line 1, in <module> |
| 403 | + TypeError: f() takes 3 positional arguments but 4 were given |
| 404 | + >>> |
| 405 | + |
| 406 | +در ادامه به ارایه انواع سینتکسهای مورد قبول پایتون در تطابق آرگومانها (Argument Matching) با پارامترهای تابع میپردازیم: |
| 407 | + |
| 408 | +* سینتکس معمول که تاکنون استفاده میکردیم یعنی به صراحت در ازای هر پارامتر یک آرگومان نظیر ارسال گردد. عمل تطابق در این سینتکس بر اساس موقعیت آرگومانها انجام میشود که در نتیجه میبایست ترتیب آرگومانها، متناظر با ترتیب پارامترها در سرآیند تابع باشد:: |
| 409 | + |
| 410 | + >>> def f(a, b, c): |
| 411 | + ... print(a, b, c) |
| 412 | + ... |
| 413 | + >>> f(1, 2, 3) |
| 414 | + 1 2 3 |
| 415 | + |
| 416 | + |
| 417 | +* سینتکس نام=مقدار، در این سینتکس آرگومانها به نام پارامترها انتساب داده میشوند و از آنجا که عمل تطابق بر اساس نام پارامترها انجام میشود دیگر موقعیت یا ترتیب آرگومانها اهمیتی ندارد:: |
| 418 | + |
| 419 | + >>> def f(a, b, c): |
| 420 | + ... print(a, b, c) |
| 421 | + ... |
| 422 | + >>> f(a=1, c=3, b=2) |
| 423 | + 1 2 3 |
| 424 | + |
| 425 | + میتوان از این دو سینتکس به صورت ترکیبی نیز استفاده کرد. فقط باید توجه داشت آرگومانهایی که عمل تطابق آنها وابسته به موقعیت است را - با رعایت ترتیب موارد قبلتر از آن - در ابتدا قرار دهیم. به مثال پایین توجه نمایید:: |
| 426 | + |
| 427 | + |
| 428 | + >>> def f(a, b, c): |
| 429 | + ... print(a, b, c) |
| 430 | + ... |
| 431 | + >>> f(1, c=3, b=2) |
| 432 | + 1 2 3 |
| 433 | + >>> f(1, 2, c=3) |
| 434 | + 1 2 3 |
| 435 | + >>> f(1, 2, 3) |
| 436 | + 1 2 3 |
| 437 | + |
| 438 | + برای تابع مثال بالا، حالتهای فراخوانی پایین نادرست هستند:: |
| 439 | + |
| 440 | + >>> f(c=3, b=2, 1) |
| 441 | + File "<stdin>", line 1 |
| 442 | + SyntaxError: positional argument follows keyword argument |
| 443 | + |
| 444 | + >>> f(a=1, 2, c=3) |
| 445 | + File "<stdin>", line 1 |
| 446 | + SyntaxError: positional argument follows keyword argument |
| 447 | + |
| 448 | + >>> f(a=1, 2, 3) |
| 449 | + File "<stdin>", line 1 |
| 450 | + SyntaxError: positional argument follows keyword argument |
| 451 | + |
| 452 | + >>> f(2, a=1, c=3) |
| 453 | + Traceback (most recent call last): |
| 454 | + File "<stdin>", line 1, in <module> |
| 455 | + TypeError: f() got multiple values for argument 'a' |
| 456 | + |
| 457 | + |
| 458 | +* سینتکس ``iterable*``، در این سینتکس یک شی از نوع تکرارپذیر (iterable - درس نهم) مانند انواع رشته، تاپل، لیست و... که توسط یک کاراکتر ستاره ``*`` نشانهگذاری شده است، به تابع ارسال میگردد. در این صورت بر اساس ترتیب موقعیت، اعضای درون شی تکرارپذیر به پارامترهای تابع اختصاص مییابند:: |
| 459 | + |
| 460 | + >>> def f(a, b, c): |
| 461 | + ... print(a, b, c) |
| 462 | + ... |
| 463 | + >>> b = [1, 2, 3] |
| 464 | + >>> f(*b) |
| 465 | + 1 2 3 |
| 466 | + |
| 467 | + |
| 468 | +* سینتکس ``dict**``، در این سینتکس یک شی دیکشنری که توسط دو کاراکتر ستاره ``**`` نشانهگذاری شده است به تابع ارسال میشود. کلیدهای این شی دیکشنری میبایست همنام با پارامترهای تعریف شده در سرآیند تابع باشند. پس از فراخوانی تابع، این شی دیکشنری باز میشود و بر اساس نام کلید در جفتهای کلید:مقدار درون آن، پارامترهای تابع مقداردهی میشوند:: |
| 469 | + |
| 470 | + >>> def f(a, b, c): |
| 471 | + ... print(a, b, c) |
| 472 | + ... |
| 473 | + >>> b = {'a':1, 'c':3, 'b':2} |
| 474 | + >>> f(**b) |
| 475 | + 1 2 3 |
| 476 | + |
| 477 | +این چهار سینتکس بر اعمال در هنگام فراخوانی تابع بحث میکنند و در تمام آنها میبایست تعداد آرگومانهای ارسالی با تعداد پارامترهای تعریف شده در سرآیند تابع برابر باشد و البته بدیهی است که در دو سینتکس پایانی لازم است تعداد اعضای شی تکرارپذیر یا تعداد جفتهای کلید:مقدار شی دیکشنری با تعداد پارامترهای تابع برابر باشند. |
| 478 | + |
| 479 | +در ادامه به ارایه سینتکسهایی در این زمینه میپردازیم که در تعریف پارامترهای تابع نقش دارند. |
272 | 480 |
|
273 | 481 |
|
274 | 482 |
|
|
0 commit comments