কেন কোনও ফাংশন কলকারীর দ্বারা উপলব্ধ হিসাবে কিছু যুক্তি সংশোধন করতে পারে, তবে অন্যরা নয়?


181

আমি ভ্যারিয়েবল স্কোপে পাইথনের দৃষ্টিভঙ্গি বোঝার চেষ্টা করছি। এই উদাহরণে, কেন এর মধ্যে অনুমিত হিসাবে f()এর মান পরিবর্তন করতে সক্ষম , তবে এর মান নয় ?xmain()n

def f(n, x):
    n = 2
    x.append(4)
    print('In f():', n, x)

def main():
    n = 1
    x = [0,1,2,3]
    print('Before:', n, x)
    f(n, x)
    print('After: ', n, x)

main()

আউটপুট:

Before: 1 [0, 1, 2, 3]
In f(): 2 [0, 1, 2, 3, 4]
After:  1 [0, 1, 2, 3, 4]

7
এখানে ভাল ব্যাখ্যা করেছেন nedbatchelder.com/text/names.html
রওশন

উত্তর:


209

কিছু উত্তরে একটি ফাংশন কলের প্রসঙ্গে "কপি" শব্দটি থাকে। আমি এটি বিভ্রান্তিকর মনে।

পাইথন কপি নেই বস্তু একটি ফাংশন কলের সময় আপনি পাস কি কখনো

ফাংশন পরামিতি নাম । আপনি যখন কোনও ফাংশন কল করেন পাইথন এই প্যারামিটারগুলিকে আপনি যে কোনও অবজেক্টে পাস করেন (কলার স্কোপের নামের মাধ্যমে) আবদ্ধ করে।

অবজেক্টগুলি পরিবর্তনযোগ্য (তালিকার মতো) বা অপরিবর্তনীয় হতে পারে (পাইগনের পূর্ণসংখ্যা যেমন স্ট্রিং)। পরিবর্তনীয় অবজেক্ট আপনি পরিবর্তন করতে পারেন। আপনি কোনও নাম পরিবর্তন করতে পারবেন না, আপনি কেবল এটি অন্য কোনও বস্তুর সাথে আবদ্ধ করতে পারেন।

আপনার উদাহরণটি স্কোপ বা নেমস্পেস সম্পর্কে নয় , এটি পাইথনের কোনও বস্তুর নামকরণ এবং বাঁধাই এবং পরিবর্তন করতে হবে

def f(n, x): # these `n`, `x` have nothing to do with `n` and `x` from main()
    n = 2    # put `n` label on `2` balloon
    x.append(4) # call `append` method of whatever object `x` is referring to.
    print('In f():', n, x)
    x = []   # put `x` label on `[]` ballon
    # x = [] has no effect on the original list that is passed into the function

অন্যান্য ভাষার ভেরিয়েবল এবং পাইথনের নামের মধ্যে পার্থক্য সম্পর্কে এখানে সুন্দর ছবি ।


3
এই নিবন্ধটি আমাকে সমস্যাটি আরও ভালভাবে বুঝতে সাহায্য করেছে এবং এটি একটি কার্যকরী এবং কিছু উন্নত ব্যবহারের পরামর্শ দেয়: পাইথনে ডিফল্ট প্যারামিটার মান
জিফাই

@ জিফাই, আমি এর আগেও এরকম উদাহরণ দেখেছি কিন্তু আমার কাছে এটি বাস্তব-বিশ্বের পরিস্থিতি বর্ণনা করে না। আপনি যদি এমন কিছু সংশোধন করে থাকেন যা এতে উত্তীর্ণ হয় তবে এটি ডিফল্ট দেওয়ার অর্থ হবে না।
মার্ক রান্সম

@MarkRansom, আমি মনে করি যদি আপনি হিসাবে ঐচ্ছিক আউটপুট গন্তব্য প্রদান করতে চান এটা জানার জন্য না: def foo(x, l=None): l=l or []; l.append(x**2); return l[-1]
জানুস লেনার

সেবাস্তিয়ান কোডের শেষ লাইনের জন্য, এটি বলেছে "# উপরের মূল তালিকার কোনও প্রভাব নেই"। তবে আমার মতে এটি কেবল "এন"-তে কোনও প্রভাব ফেলবে না, তবে মূল () ফাংশনে "x" পরিবর্তন করেছে। আমি কি সঠিক?
ব্যবহারকারী 17670

1
@ user17670: x = []মধ্যে f()তালিকায় কোনো প্রভাব নেই xপ্রধান ফাংশন হবে। আমি মন্তব্যটি আপডেট করেছি, এটি আরও নির্দিষ্ট করে তুলতে।
jfs

15

আপনি ইতিমধ্যে বেশ কয়েকটি উত্তর পেয়েছেন, এবং আমি জেএফ সেবাস্তিয়ানের সাথে বিস্তৃতভাবে একমত, তবে আপনি এটি একটি শর্টকাট হিসাবে দরকারী মনে করতে পারেন:

যে কোনও সময় আপনি দেখতে পাবেন varname =, আপনি ফাংশনের সুযোগের মধ্যে একটি নতুন নাম বাঁধাই করছেন । varnameআগে যে মানটি আবদ্ধ ছিল তা এই সুযোগের মধ্যে নষ্ট হয়ে যায়

যে কোনও সময় আপনি দেখতে পাচ্ছেন যে varname.foo()আপনি কোনও পদ্ধতিটি কল করছেন varname। পদ্ধতিটি পরিবর্তিত করতে পারে নাম (যেমন list.append)। varname(বা, বরং, varnameনাম যে বস্তুর নাম) একাধিক স্কোপে বিদ্যমান থাকতে পারে এবং যেহেতু এটি একই জিনিস, তাই কোনও পরিবর্তন সমস্ত স্কোপে দৃশ্যমান হবে।

[নোট করুন globalকীওয়ার্ডটি প্রথম ক্ষেত্রে একটি ব্যতিক্রম তৈরি করে]


13

fআসলে মানটির পরিবর্তন করে না x(যা সর্বদা তালিকার উদাহরণে একই রেফারেন্স হয়)। বরং এটি এই তালিকার বিষয়বস্তুগুলিকে পরিবর্তন করে ।

উভয় ক্ষেত্রে, একটি রেফারেন্সের একটি অনুলিপি ফাংশনে প্রেরণ করা হয়। ফাংশনের অভ্যন্তরে,

  • nএকটি নতুন মান বরাদ্দ করা হয়। কেবলমাত্র ফাংশনের অভ্যন্তরের রেফারেন্সটি পরিবর্তিত হয়েছে, এর বাইরে থাকা নয়।
  • xকোনও নতুন মান বরাদ্দ পাওয়া যায় না: ফাংশনের ভিতরে বা বাইরে রেফারেন্সটিও পরিবর্তিত হয় না। পরিবর্তে, xএর মান পরিবর্তন করা হয়েছে।

যেহেতু xফাংশনের অভ্যন্তর এবং এর বাইরে উভয়ই একই মান বোঝায়, উভয়ই পরিবর্তনটি দেখতে পান। বিপরীতে, nফাংশনের অভ্যন্তরে এবং এর বাইরে ফাংশনের অভ্যন্তরে পুনর্নির্দিষ্ট হওয়ার পরে এটি বিভিন্ন মানকে বোঝায় n


8
"অনুলিপি" বিভ্রান্তিকর। পাইথনের সি এর মত ভেরিয়েবল নেই Py পাইথনের সমস্ত নামই রেফারেন্স। আপনি নামটি সংশোধন করতে পারবেন না, আপনি কেবল এটি অন্য কোনও বস্তুর সাথে আবদ্ধ করতে পারেন, এগুলিই। পাইথনে পরিবর্তিত ও অপরিবর্তনীয় বস্তুর বিষয়ে কথা বলাই কেবল বুদ্ধিমান হয়ে যায় তারা নাম নয়।
jfs

1
@ জেএফ সেবাস্তিয়ান: আপনার বক্তব্যটি সর্বোত্তমভাবে বিভ্রান্তিকর। সংখ্যা হিসাবে উল্লেখ হিসাবে বিবেচনা দরকারী নয়।
পিতরৌ

9
@ ডিসফান্টেক্টর: সংখ্যাগুলি হ'ল অপরিবর্তনীয় বস্তুর উল্লেখ। আপনি যদি এগুলির পরিবর্তে অন্য কোনও উপায়ে ভাবতে চান তবে আপনার কাছে ব্যাখ্যা করার জন্য একগুচ্ছ অদ্ভুত বিশেষ কেস রয়েছে। আপনি যদি এগুলি অপরিবর্তনীয় বলে মনে করেন তবে কোনও বিশেষ ক্ষেত্রে নেই।
এসলট

@ এস.লোট: হুডের নীচে যা চলছে তা নির্বিশেষে, গাইডো ভ্যান রসুম পাইথন ডিজাইনিং করার জন্য প্রচুর প্রচেষ্টা করেছিলেন যাতে প্রোগ্রামার সংখ্যাকে কেবল ... সংখ্যা হিসাবে চিহ্নিত করতে পারে।
পিতরৌ

1
@ জেএফ, রেফারেন্সটি অনুলিপি করা হয়েছে।
habnabit

7

বিভ্রান্তি কমাতে আমি পরিবর্তনশীলগুলির নাম পরিবর্তন করব। n -> nf বা nmainx -> এক্সএফ বা এক্সমেইন :

def f(nf, xf):
    nf = 2
    xf.append(4)
    print 'In f():', nf, xf

def main():
    nmain = 1
    xmain = [0,1,2,3]
    print 'Before:', nmain, xmain
    f(nmain, xmain)
    print 'After: ', nmain, xmain

main()

আপনি যখন ফাংশন কল , পাইথন রানটাইম একটি কপি করে তোলে xmain প্রয়োজন এবং এটা নির্ধারণ XF , এবং একইভাবে একটি কপি নির্ধারণ nmain করার NF

এন এর ক্ষেত্রে , যে মানটি অনুলিপি করা হয় তা হ'ল 1।

X এর ক্ষেত্রে যে মানটি অনুলিপি করা হয় তা আক্ষরিক তালিকা নয় [0, 1, 2, 3] । এটি সেই তালিকার একটি রেফারেন্সxf এবং xmain একই তালিকার দিকে ইঙ্গিত করছে, তাই আপনি xf সংশোধন করার সময় আপনি xmain পরিবর্তন করছেন

তবে, আপনি যদি কিছু লিখতে চান:

    xf = ["foo", "bar"]
    xf.append(4)

আপনি দেখতে পাবেন যে এক্সমাইন পরিবর্তন হয়নি। এটি কারণ, xf = ["foo", "বার"] লাইনে আপনার xf পরিবর্তন করে একটি নতুন তালিকার দিকে নির্দেশ করেছেন । এই নতুন তালিকায় আপনি যে কোনও পরিবর্তন করেন তার তালিকাটিতে xmain এখনও নির্দেশ করে এমন কোনও প্রভাব ফেলবে না।

আশা করি এইটি কাজ করবে. :-)


2
"এন এর ক্ষেত্রে, যে মানটি অনুলিপি করা হয়েছে ..." - এটি ভুল, এখানে কোনও অনুলিপি করা হয়নি (যদি আপনি উল্লেখগুলি গণনা না করেন)। পরিবর্তে, পাইথন 'নাম' ব্যবহার করে যা প্রকৃত অবজেক্টগুলিকে নির্দেশ করে। nf এবং xf nmain এবং xmain এ নির্দেশ করে, যতক্ষণ না nf = 2, নামটি nfপয়েন্টে পরিবর্তিত করা হয় 2। নম্বরগুলি পরিবর্তনযোগ্য, তালিকাগুলি পরিবর্তনযোগ্য mut
কেসি কুবাল

2

এটি কারণ একটি তালিকা একটি পরিবর্তনীয় অবজেক্ট। আপনি [0,1,2,3] এর মান x নির্ধারণ করছেন না, আপনি [0,1,2,3] অবজেক্টে একটি লেবেল সংজ্ঞায়িত করছেন।

আপনার নিজের ফাংশনটি এফ () হিসাবে ঘোষণা করা উচিত:

def f(n, x=None):
    if x is None:
        x = []
    ...

3
এর পরিবর্তনশীলতার সাথে কোনও সম্পর্ক নেই। আপনি যদি এর x = x + [4]পরিবর্তে এটি x.append(4)করতে চান তবে আপনি কলকারীটিতে কোনও পরিবর্তন দেখতে পাবেন না যদিও একটি তালিকা পরিবর্তনযোগ্য। এটি যদি সত্যই পরিবর্তিত হয় তবে তা করতে হবে।
glglgl

1
OTOH, আপনি যদি তা করেন x += [4]তবে xমিউটেশন করা হয় ঠিক যেমনটি ঘটে থাকে x.append(4), তাই কলার পরিবর্তনটি দেখতে পাবেন।
পিএম 2 রিং

2

এন একটি অন্তর্নিহিত (অপরিবর্তনীয়), এবং একটি অনুলিপি ফাংশনে প্রেরণ করা হয়, সুতরাং ফাংশনে আপনি অনুলিপিটি পরিবর্তন করছেন।

এক্স হল একটি তালিকা (পরিবর্তনযোগ্য), এবং পয়েন্টারের একটি অনুলিপি ফাংশনটি পাস করেছে যাতে x.append (4) তালিকার বিষয়বস্তু পরিবর্তন করে। তবে, আপনি নিজের ফাংশনে x = [0,1,2,3,4] বলেছিলেন, আপনি x এর মূল বিষয়বস্তু পরিবর্তন করবেন না ()।


3
"পয়েন্টারের অনুলিপি" ফ্রেসসিং দেখুন। উভয় জায়গাতেই বস্তুর উল্লেখ পাওয়া যায়। n হ'ল স্থাবর বস্তুর একটি উল্লেখ; এক্স একটি পরিবর্তনীয় অবজেক্টের একটি রেফারেন্স।
এস্লট

2

যদি ফাংশনগুলি সম্পূর্ণ ভিন্ন ভেরিয়েবলগুলির সাথে পুনরায় লিখিত হয় এবং আমরা সেগুলিতে আইডি কল করি তবে এটি পয়েন্টটি ভালভাবে চিত্রিত করে। আমি প্রথমে এটি পাইনি এবং দুর্দান্ত ব্যাখ্যা সহ জেএফএসের পোস্টটি পড়ি , তাই আমি নিজেকে বোঝার / বোঝানোর চেষ্টা করেছি:

def f(y, z):
    y = 2
    z.append(4)
    print ('In f():             ', id(y), id(z))

def main():
    n = 1
    x = [0,1,2,3]
    print ('Before in main:', n, x,id(n),id(x))
    f(n, x)
    print ('After in main:', n, x,id(n),id(x))

main()
Before in main: 1 [0, 1, 2, 3]   94635800628352 139808499830024
In f():                          94635800628384 139808499830024
After in main: 1 [0, 1, 2, 3, 4] 94635800628352 139808499830024

z এবং x এর একই আইডি রয়েছে। নিবন্ধটি বলে ঠিক একই অন্তর্নিহিত কাঠামোর জন্য আলাদা আলাদা ট্যাগ।


0

পাইথন হ'ল একটি খাঁটি পাস-বাই-ভ্যালু ভাষা এটি যদি আপনি সঠিকভাবে চিন্তা করেন। পাইথন ভেরিয়েবল স্মৃতিতে কোনও বস্তুর অবস্থান সঞ্চয় করে। পাইথন ভেরিয়েবল বস্তুটি নিজেই সঞ্চয় করে না। আপনি যখন কোনও ফাংশনে কোনও ভেরিয়েবল পাস করেন, আপনি একটি অনুলিপি পাস করছেন ভেরিয়েবল দ্বারা চিহ্নিত বস্তুর ঠিকানার ।

এই দুটি ফাংশন বিপরীতে

def foo(x):
    x[0] = 5

def goo(x):
    x = []

এখন, যখন আপনি শেলটি টাইপ করেন

>>> cow = [3,4,5]
>>> foo(cow)
>>> cow
[5,4,5]

গু এর সাথে এই তুলনা করুন।

>>> cow = [3,4,5]
>>> goo(cow)
>>> goo
[3,4,5]

প্রথম ক্ষেত্রে, আমরা foo এর কাছে গরুর ঠিকানার একটি অনুলিপি পাস করি এবং সেখানে থাকা বস্তুর অবস্থাটি পরিমার্জন করে। বস্তুটি সংশোধিত হয়।

দ্বিতীয় ক্ষেত্রে আপনি গরুর ঠিকানার একটি অনুলিপি গুগুলিকে দিতে পারেন। তারপরে গুড সেই কপিটি পরিবর্তন করতে এগিয়ে যায়। প্রভাব: কিছুই না।

আমি এটাকে গোলাপী ঘরের নীতি বলি । আপনি যদি নিজের ঠিকানার একটি অনুলিপি তৈরি করেন এবং কোনও চিত্রশিল্পীকে সেই ঠিকানায় গোলাপী রঙে ঘর আঁকতে বলে থাকেন, তবে আপনি গোলাপী ঘরটি শেষ করবেন। যদি আপনি চিত্রককে আপনার ঠিকানার একটি অনুলিপি দেন এবং তাকে এটি নতুন ঠিকানায় পরিবর্তন করতে বলেন, আপনার বাড়ির ঠিকানা পরিবর্তন হয় না।

ব্যাখ্যাটি অনেক বিভ্রান্তি দূর করে। পাইথন ঠিকানা অনুসারে ভেরিয়েবল স্টোরটি পাস করে।


পয়েন্টার মান দ্বারা একটি খাঁটি পাস যদি আপনি এটি সঠিকভাবে সম্পর্কে চিন্তা করেন তবে রেফারেন্সের পাসের চেয়ে খুব আলাদা নয় ...
গ্যালেনেট

গু তাকান। আপনি যদি রেফারেন্স অনুসারে খাঁটি পাস হন তবে এটির যুক্তিটি পরিবর্তিত হত। না, পাইথন কোনও খাঁটি পাস বাই রেফারেন্স ভাষা নয়। এটি মান অনুসারে রেফারেন্সগুলি পাস করে।
ncmathsadist

0

পাইথন রেফারেন্সের মান দ্বারা অনুলিপি করা হয়। কোনও বস্তু স্মৃতিতে একটি ক্ষেত্র দখল করে এবং সেই রেফারেন্সটি সেই বস্তুর সাথে সম্পর্কিত তবে এটি মেমরির ক্ষেত্রে একটি ক্ষেত্র দখল করে। এবং নাম / মান একটি রেফারেন্সের সাথে জড়িত। পাইথন ফাংশনে, এটি সর্বদা রেফারেন্সের মানটি অনুলিপি করে, সুতরাং আপনার কোডে n একটি নতুন নাম হিসাবে অনুলিপি করা হয়, যখন আপনি এটি বরাদ্দ করেন, এতে কলার স্ট্যাকের একটি নতুন স্থান থাকে। তবে তালিকার জন্য, নামটিও অনুলিপি করা হয়েছে, তবে এটি একই মেমরিটিকে উল্লেখ করে (যেহেতু আপনি কখনই তালিকাটিকে কোনও নতুন মান নির্ধারণ করেন না)। অজগরের এক ম্যাজিক!


0

আমার সাধারণ বোধগম্যতা হল যে কোনও বস্তুর পরিবর্তনশীল (যেমন একটি তালিকা বা ডিক হিসাবে অন্যদের মধ্যে) এর কাজগুলির মাধ্যমে সংশোধন করা যায়। আমি বিশ্বাস করি আপনি যা করতে সক্ষম নন তা হ'ল প্যারামিটারটি পুনরায় নিয়োগ করা - অর্থাত, একটি কলযোগ্য ফাংশনটির মধ্যে রেফারেন্স দিয়ে এটি বরাদ্দ করুন।

এটি অন্যান্য অনেক ভাষার সাথে সামঞ্জস্যপূর্ণ।

এটি কীভাবে কাজ করে তা দেখতে নীচের শর্ট স্ক্রিপ্টটি চালান:

def func1(x, l1):
    x = 5
    l1.append("nonsense")

y = 10
list1 = ["meaning"]
func1(y, list1)
print(y)
print(list1)

-3

আমি আমার উত্তর কয়েকবার পরিবর্তন করেছি এবং বুঝতে পেরেছিলাম যে আমাকে কিছু বলতে হবে না, অজগরটি ইতিমধ্যে নিজেই ব্যাখ্যা করেছিল।

a = 'string'
a.replace('t', '_')
print(a)
>>> 'string'

a = a.replace('t', '_')
print(a)
>>> 's_ring'

b = 100
b + 1
print(b)
>>> 100

b = b + 1
print(b)
>>> 101

def test_id(arg):
    c = id(arg)
    arg = 123
    d = id(arg)
    return

a = 'test ids'
b = id(a)
test_id(a)
e = id(a)

# b = c  = e != d
# this function do change original value
del change_like_mutable(arg):
    arg.append(1)
    arg.insert(0, 9)
    arg.remove(2)
    return

test_1 = [1, 2, 3]
change_like_mutable(test_1)



# this function doesn't 
def wont_change_like_str(arg):
     arg = [1, 2, 3]
     return


test_2 = [1, 1, 1]
wont_change_like_str(test_2)
print("Doesn't change like a imutable", test_2)

এই শয়তানটি কোনও রেফারেন্স / মান / পরিবর্তনযোগ্য বা না / উদাহরণ নয়, নাম স্থান বা পরিবর্তনশীল / তালিকা বা স্ট্রিং নয়, এটি সিনট্যাক্স, সমান স্বাক্ষর।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.