কোনও তালিকা পুনরাবৃত্তি করার সময় পাইথন কেবলমাত্র পৃথক উপাদানের একটি অনুলিপি তৈরি করে?


31

আমি ঠিক বুঝতে পেরেছি পাইথনে, যদি কেউ লিখেন

for i in a:
    i += 1

আসল তালিকার উপাদানগুলি aআসলে কিছুতেই প্রভাব ফেলবে না, যেহেতু ভেরিয়েবলটি iমূল উপাদানটির অনুলিপি হিসাবে পরিণত হয় a

মূল উপাদানটি সংশোধন করার জন্য,

for index, i in enumerate(a):
    a[index] += 1

প্রয়োজন হবে।

আমি এই আচরণে সত্যিই অবাক হয়েছিলাম। এটি খুব বিপরীত বলে মনে হচ্ছে, অন্য ভাষা থেকে পৃথক বলে মনে হচ্ছে এবং আমার কোডটিতে এমন ত্রুটি রয়েছে যার ফলে আমাকে আজ দীর্ঘকাল ধরে ডিবাগ করতে হয়েছিল।

আমি পাইথন টিউটোরিয়াল আগে পড়েছি। কেবল নিশ্চিত হতেই, আমি ঠিক এখনই বইটি আবার পরীক্ষা করে দেখেছি এবং এটি এই আচরণের মোটেও উল্লেখ করে না।

এই নকশার পিছনে যুক্তি কী? টিউটোরিয়ালটি বিশ্বাস করে যে এটি পাঠকদের স্বাভাবিকভাবেই পাওয়া উচিত? পুনরাবৃত্তির উপর অন্য কোন ভাষায় একই আচরণ উপস্থিত রয়েছে, যা ভবিষ্যতে আমার মনোযোগ দেওয়া উচিত?


19
কেবলমাত্র সত্য যদি iতা পরিবর্তনযোগ্য হয় বা আপনি একটি পরিবর্তন না করা অপারেশন চালিয়ে যাচ্ছেন। নেস্টেড তালিকার for i in a: a.append(1)সাথে আলাদা আচরণ থাকবে; পাইথন নেস্টেড তালিকাগুলি অনুলিপি করে না । তবে পূর্ণসংখ্যা পরিবর্তনযোগ্য এবং সংযোজন একটি নতুন অবজেক্ট ফিরিয়ে দেয়, এটি পুরানোটিকে পরিবর্তন করে না।
jonrsharpe

10
মোটেও অবাক হওয়ার কিছু নেই। আমি এমন ভাষার কথা ভাবতে পারি না যা পূর্ণসংখ্যার মতো মৌলিক ধরণের অ্যারের জন্য ঠিক একই রকম হয় না। উদাহরণস্বরূপ, জাভাস্ক্রিপ্ট চেষ্টা করুন a=[1,2,3];a.forEach(i => i+=1);alert(a)। সি #
এডক 65

7
আপনি i = i + 1প্রভাবিত করতে আশা করবেন a?
ডেলটাব

7
মনে রাখবেন যে অন্যান্য ভাষায় এই আচরণটি আলাদা নয়। সি, জাভাস্ক্রিপ্ট, জাভা ইত্যাদি এইভাবে আচরণ করে।
slebetman

1
@ jonrsharpe তালিকাগুলির জন্য "+ =" পুরানো তালিকা পরিবর্তন করা হয়েছে, যখন "+" একটি নতুন তৈরি করেছে
ভ্যাসিলি আলেক্সিভ

উত্তর:


68

আমি ইতিমধ্যে ইতিমধ্যে একটি অনুরূপ প্রশ্নের উত্তর দিয়েছি এবং এটি উপলব্ধি করা খুব গুরুত্বপূর্ণ যে এর +=বিভিন্ন অর্থ হতে পারে:

  • যদি ডেটা টাইপ ইন-প্লেস অ্যাডিশন প্রয়োগ করে (যেমন একটি সঠিকভাবে __iadd__কার্যকারী ফাংশন রয়েছে) তবে যে ডেটাটি iউল্লেখ করা হয় তা আপডেট হয় (এটি কোনও তালিকায় বা অন্য কোথাও থাকুক না কেন)।

  • যদি ডেটা টাইপ কোনও __iadd__পদ্ধতি প্রয়োগ না করে তবে i += xস্টেটমেন্টটি কেবল সিনট্যাকটিক চিনির জন্য i = i + x, সুতরাং একটি নতুন মান তৈরি করা হয় এবং ভেরিয়েবলের নাম নির্ধারিত হয় i

  • যদি ডেটা টাইপ প্রয়োগ করে __iadd__তবে এটি অদ্ভুত কিছু করে। এটি সম্ভবত আপডেট হতে পারে ... না - এটি সেখানে কী বাস্তবায়িত হয় তার উপর নির্ভর করে।

পাইথনস পূর্ণসংখ্যা, ফ্লোটস, স্ট্রিংগুলি কার্যকর করে না __iadd__তাই এগুলি যথাস্থানে আপডেট করা হবে না। তবে অন্যান্য ডেটা ধরণের numpy.arrayবা listএটি প্রয়োগ করে এবং আপনার প্রত্যাশার মতো আচরণ করবে। সুতরাং এটি পুনরুক্তির সময় অনুলিপি বা নকল-কপির বিষয় নয় (সাধারণত এটি listএস এবং এসগুলির জন্য অনুলিপি করে না tuple- তবে এটি পাত্রে __iter__এবং __getitem__পদ্ধতির প্রয়োগের উপরও নির্ভর করে !) - এটি ডেটা টাইপের আরও বিষয় আপনি আপনার জমা আছে a


2
প্রশ্নে বর্ণিত আচরণের এটি সঠিক ব্যাখ্যা।
pabouk

19

স্পষ্টকরণ - পরিভাষা

পাইথন রেফারেন্স এবং পয়েন্টার ধারণাগুলির মধ্যে পার্থক্য করে না । এগুলি সাধারণত রেফারেন্স শব্দটি ব্যবহার করে তবে আপনি যদি সি ++ এর মতো ভাষার সাথে এই পার্থক্যটির তুলনা করেন - এটি কোনও পয়েন্টারের কাছে অনেক বেশি কাছাকাছি ।

যেহেতু প্রশ্নকর্তা স্পষ্টত সি ++ ব্যাকগ্রাউন্ড থেকে এসেছেন, এবং যে পার্থক্যটি - যা ব্যাখ্যাটির জন্য প্রয়োজনীয় - পাইথনে অস্তিত্ব নেই , তাই আমি সি ++ এর পরিভাষা ব্যবহার করার সিদ্ধান্ত নিয়েছি, যা:

  • মান : আসল ডেটা যা মেমরিতে বসে। void foo(int x);হ'ল এমন কোনও ফাংশনের স্বাক্ষর যা মান অনুসারে পূর্ণসংখ্যা পায় ।
  • পয়েন্টার : একটি মেমরি ঠিকানা মান হিসাবে গণ্য করা হয়। এটিকে নির্দেশ করে মেমরিটি অ্যাক্সেস করতে পিছিয়ে দেওয়া যেতে পারে। void foo(int* x);একটি ফাংশনের স্বাক্ষর যা পয়েন্টার অনুসারে একটি পূর্ণসংখ্যা গ্রহণ করে
  • তথ্যসূত্র : পয়েন্টারগুলির আশেপাশে চিনি। পর্দার পিছনে একটি পয়েন্টার রয়েছে, তবে আপনি কেবল স্থগিত মানটি অ্যাক্সেস করতে পারেন এবং এটি যে ঠিকানাটি দেখায় তাতে পরিবর্তন করতে পারবেন না। void foo(int& x);একটি ফাংশনের স্বাক্ষর যা রেফারেন্স দ্বারা একটি পূর্ণসংখ্যার প্রাপ্ত হয়

"অন্যান্য ভাষা থেকে আলাদা" বলতে কী বোঝ? বেশিরভাগ ভাষাগুলি যা আমি জানি যে প্রতিটি লুপের জন্য সমর্থনগুলি সেই উপাদানটি অনুলিপি করছে যদি না অন্যথায় নির্দেশ না দেওয়া হয়।

বিশেষত পাইথনের জন্য (যদিও এই কারণগুলির মধ্যে অনেকগুলি একই রকম স্থাপত্য বা দার্শনিক ধারণা সহ অন্যান্য ভাষায় প্রয়োগ হতে পারে):

  1. এই আচরণটি অজানা লোকদের জন্য বাগগুলি সৃষ্টি করতে পারে তবে বিকল্প আচরণ এমনকি এটি সম্পর্কে যারা সচেতন তাদের জন্যও বাগের কারণ হতে পারে । যখন আপনি কোনও ভেরিয়েবল ( i) নির্ধারণ করেন আপনি সাধারণত থামেন না এবং অন্য সমস্ত ভেরিয়েবলগুলি বিবেচনা করবেন যা এর কারণে পরিবর্তিত হবে ( a)। আপনি যে স্কোপটিতে কাজ করছেন তা সীমাবদ্ধ করা স্প্যাগেটি কোড প্রতিরোধের একটি প্রধান কারণ, এবং তাই অনুলিপি দ্বারা পুনরাবৃত্তি সাধারণত এমন ভাষাগুলিতেও ডিফল্ট যেগুলি রেফারেন্স দ্বারা পুনরাবৃত্তি সমর্থন করে।

  2. পাইথন ভেরিয়েবলগুলি সর্বদা একক পয়েন্টার হয়, সুতরাং অনুলিপি দ্বারা পুনরাবৃত্তি করা সস্তা - রেফারেন্স অনুসারে পুনরাবৃত্তি করার চেয়ে সস্তা, যার প্রতিটি সময় আপনি মূল্যটি অ্যাক্সেস করার সময় অতিরিক্ত মুলতবি প্রয়োজন।

  3. পাইথনের রেফারেন্স ভেরিয়েবলের ধারণা নেই - যেমন - সি ++। অর্থ্যাৎ পাইথনের সমস্ত ভেরিয়েবল আসলে তথ্যসূত্র, তবে যে অর্থে সেগুলি পয়েন্টার - সি ++ type& nameআর্গুমেন্টের মতো কোনও নেপথ্য দৃশ্যের কনস্টেট রেফারেন্স নয় । যেহেতু এই ধারণাটি পাইথনে বিদ্যমান নেই, রেফারেন্স দ্বারা পুনরাবৃত্তি প্রয়োগ করে - এটি এটিকে ডিফল্ট হিসাবে তৈরি করা যাক! - বাইটোকোডে আরও জটিলতা যুক্ত করতে হবে।

  4. পাইথনের forবিবৃতি কেবল অ্যারেগুলিতেই নয়, জেনারেটরের আরও সাধারণ ধারণার উপরেও কাজ করে। পর্দার আড়ালে, পাইথন iterআপনার অ্যারেগুলিকে একটি অবজেক্ট পেতে কল nextকরে - যা আপনি যখন ডাকে তখন হয় পরবর্তী উপাদানটি ফেরত দেয় বা raiseসা StopIteration। পাইথনে জেনারেটরগুলি প্রয়োগ করার বিভিন্ন উপায় রয়েছে এবং পুনরাবৃত্তি-দ্বারা-রেফারেন্সের জন্য এগুলি কার্যকর করা আরও অনেক কঠিন হত।


উত্তর করার জন্য ধন্যবাদ. দেখে মনে হচ্ছে যে পুনরাবৃত্তকারীদের সম্পর্কে আমার বোঝাপড়া তখনও যথেষ্ট শক্ত নয়। ডিফল্টরূপে সি ++ রেফারেন্সে পুনরাবৃত্তিকারীরা কি নয়? যদি আপনি পুনরুক্তিটিকে অবজ্ঞা করেন আপনি সর্বদা অবিলম্বে মূল ধারকটির উপাদানটির মান পরিবর্তন করতে পারবেন?
এক্সজি

4
পাইথন করে রেফারেন্স দ্বারা বারবার (অবশ্য মান কিন্তু মান একটি রেফারেন্স)। পরিবর্তনীয় অবজেক্টের তালিকার সাথে এটি চেষ্টা করে দ্রুত প্রমাণ করা হবে যে কোনও অনুলিপি ঘটে না।
jonrsharpe

সি ++ এর আইট্রেটারগুলি আসলে এমন বস্তু যা অ্যারেতে মানটি অ্যাক্সেস করার জন্য পিছিয়ে যেতে পারে। আসল উপাদানটি সংশোধন করতে, আপনি ব্যবহার করেন *it = ...- তবে এই ধরণের সিনট্যাক্সটি ইতিমধ্যে ইঙ্গিত দেয় যে আপনি অন্য কোথাও কিছু পরিবর্তন করছেন - যা # 1 ইস্যুকে কম কারণ হিসাবে চিহ্নিত করে। # 2 এবং # 3 কারণগুলিও প্রযোজ্য নয়, কারণ সি ++ এ অনুলিপি ব্যয়বহুল এবং রেফারেন্স ভেরিয়েবলের ধারণা বিদ্যমান। কারণ হিসাবে # 4 - একটি রেফারেন্স ফিরিয়ে দেওয়ার ক্ষমতা সমস্ত ক্ষেত্রে সহজ বাস্তবায়নের অনুমতি দেয়।
ইদান আরে

1
@ জোনারশপে হ্যাঁ, এটি রেফারেন্সের মাধ্যমে বলা হয়েছে, তবে যে কোনও ভাষায় পয়েন্টার এবং রেফারেন্সের মধ্যে পার্থক্য রয়েছে এই ধরণের পুনরাবৃত্তি পয়েন্টার দ্বারা একটি পুনরাবৃত্তি হবে (এবং যেহেতু পয়েন্টারগুলি মান হয় - মান দ্বারা পুনরাবৃত্তি)) আমি একটি ব্যাখ্যা যোগ করব।
ইদান আরে

20
আপনার প্রথম অনুচ্ছেদে পরামর্শ দেয় যে পাইথন, অন্যান্য ভাষার মতো এই উপাদানটিকে একটি লুপের জন্য অনুলিপি করে। এটা হয় না। এটি আপনাকে সেই উপাদানটিতে পরিবর্তন করার সুযোগ সীমাবদ্ধ করে না। ওপি কেবল এই আচরণটি দেখে কারণ তাদের উপাদানগুলি পরিবর্তনযোগ্য; এমনকি এই পার্থক্যটি উল্লেখ না করেই আপনার উত্তরটি সেরা অসম্পূর্ণ এবং সবচেয়ে ভ্রান্ত।
jonrsharpe

11

পাইথনের জমিতে কেন এমনটি ঘটে তা সত্যভাবে চিত্রিত করার জন্য এখানকার উত্তরের কোনও উত্তরই আপনাকে কোনও কোড দেয় না। এবং এটি আরও গভীর পদ্ধতির দিকে নজর দেওয়া মজাদার তাই এখানে যায়।

এটি আপনার প্রত্যাশার মতো কার্যকর না হওয়ার প্রাথমিক কারণটি পাইথনে, যখন আপনি লেখেন:

i += 1

এটি আপনি যা করছেন মনে করছেন তা করছে না। পূর্ণসংখ্যা পরিবর্তনযোগ্য। পাইথনের মধ্যে বস্তুটি আসলে কী আছে তা খতিয়ে দেখলে এটি দেখা যায়:

a = 0
print('ID of the first integer:', id(a))
a += 1
print('ID of the first integer +=1:', id(a))

আইডি ফাংশনটি তার জীবদ্দশায় কোনও জিনিসের জন্য একটি অনন্য এবং ধ্রুবক মান উপস্থাপন করে। ধারণাগতভাবে, এটি সি / সি ++ এর কোনও মেমরি ঠিকানায় আলগাভাবে মানচিত্র করে। উপরের কোডটি চলছে:

ID of the first integer: 140444342529056
ID of the first integer +=1: 140444342529088

এর অর্থ প্রথমটি aআর দ্বিতীয়টির মতো নয় a, কারণ তাদের আইডি আলাদা। কার্যকরভাবে তারা স্মৃতিতে বিভিন্ন স্থানে রয়েছে।

কোনও বস্তুর সাহায্যে জিনিসগুলি ভিন্নভাবে কাজ করে। আমি +=এখানে অপারেটরটি ওভাররাইট করেছি :

class CustomInt:
  def __iadd__(self, other):
    # Override += 1 for this class
    self.value = self.value + other.value
    return self

  def __init__(self, v):
    self.value = v

ints = []
for i in range(5):
  int = CustomInt(i)
  print('ID={}, value={}'.format(id(int), i))
  ints.append(int)


for i in ints:
  i += CustomInt(i.value)

print("######")
for i in ints:
  print('ID={}, value={}'.format(id(i), i.value))

নিম্নলিখিত ফলাফলের মধ্যে এই ফলাফলগুলি চালানো:

ID=140444284275400, value=0
ID=140444284275120, value=1
ID=140444284275064, value=2
ID=140444284310752, value=3
ID=140444284310864, value=4
######
ID=140444284275400, value=0
ID=140444284275120, value=2
ID=140444284275064, value=4
ID=140444284310752, value=6
ID=140444284310864, value=8

লক্ষ্য করুন যে এই ক্ষেত্রে আইডি বৈশিষ্ট্যটি উভয় পুনরাবৃত্তির জন্য একই , যদিও বস্তুর মান পৃথক হয় (আপনি idবস্তুর ধারনকৃত মানটিরও সন্ধান করতে পারেন, যা পরিবর্তিত হওয়ার সাথে সাথে পরিবর্তিত হবে - কারণ পূর্ণসংখ্যার কারণ অপরিবর্তনীয়)।

আপনি যখন অপরিবর্তনীয় কোনও বস্তুর সাথে একই অনুশীলন পরিচালনা করেন তখন এর সাথে তুলনা করুন:

ints_primitives = []
for i in range(5):
  int = i
  ints_primitives.append(int)
  print('ID={}, value={}'.format(id(int), i))

print("######")
for i in ints_primitives:
  i += 1
  print('ID={}, value={}'.format(id(int), i))


print("######")
for i in ints_primitives:
  print('ID={}, value={}'.format(id(i), i))

এই ফলাফলগুলি:

ID=140023258889248, value=0
ID=140023258889280, value=1
ID=140023258889312, value=2
ID=140023258889344, value=3
ID=140023258889376, value=4
######
ID=140023258889280, value=1
ID=140023258889312, value=2
ID=140023258889344, value=3
ID=140023258889376, value=4
ID=140023258889408, value=5
######
ID=140023258889248, value=0
ID=140023258889280, value=1
ID=140023258889312, value=2
ID=140023258889344, value=3
ID=140023258889376, value=4

এখানে কয়েকটি বিষয় লক্ষ্য করা যায়। প্রথমত, এর সাথে লুপটিতে +=আপনি আর আসল বস্তুতে যুক্ত হবেন না। এক্ষেত্রে পাইথনের অপরিবর্তনীয় প্রকারের মধ্যে অন্তরগুলি হওয়ায় পাইথন একটি আলাদা আইডি ব্যবহার করে। এছাড়াও আকর্ষণীয় লক্ষণীয় যে পাইথন idএকই স্থায়ী মান সহ একাধিক ভেরিয়েবলের জন্য একই অন্তর্নিহিত ব্যবহার করে:

a = 1999
b = 1999
c = 1999

print('id a:', id(a))
print('id b:', id(b))
print('id c:', id(c))

id a: 139846953372048
id b: 139846953372048
id c: 139846953372048

tl; dr - পাইথনের এক মুঠো অপরিবর্তনীয় প্রকার রয়েছে, যা আপনার দেখা আচরণের কারণ হয়ে দাঁড়ায়। সমস্ত পরিবর্তনীয় ধরণের জন্য, আপনার প্রত্যাশাটি সঠিক।


6

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

for i in a:
    i += 1

আনপ্যাক করার জন্য দুটি জিনিস রয়েছে: for _ in _:বাক্য গঠন এবং _ += _বাক্য গঠন। লুপটি প্রথমে নিতে অন্য ভাষার মতো পাইথনের একটি for-eachলুপ রয়েছে যা মূলত পুনরাবৃত্ত প্যাটার্নের জন্য সিনট্যাক্স চিনির মতো। পাইথনে, একটি পুনরুক্তিকারী এমন একটি বস্তু যা এমন .__next__(self)পদ্ধতিটি সংজ্ঞায়িত করে যা ক্রমানুসারে বর্তমান উপাদানটি ফেরত দেয়, পরবর্তীটিতে অগ্রসর হয় এবং ক্রমটিতে StopIterationআর কোনও আইটেম না থাকলে একটি উত্থাপন করবে । একটি ইটেবারেবল এমন একটি বস্তু যা কোনও .__iter__(self)পদ্ধতিটি সংশোধন করে যা একটি পুনরাবৃত্তিকে দেয়

(এনবি: একটিও Iteratorএকটি Iterableএবং এটি নিজস্ব .__iter__(self)পদ্ধতি থেকে ফিরে আসে ))

পাইথনে সাধারণত একটি ইনবিল্ট ফাংশন থাকে যা কাস্টম ডাবল আন্ডারস্কোর পদ্ধতিতে প্রতিনিধিত্ব করে। সুতরাং এটির iter(o)যার সমাধান হয় o.__iter__()এবং next(o)এটি সমাধান করে o.__next__()। দ্রষ্টব্য যে এই অন্তর্নির্মিত ফাংশনগুলি প্রায়শই যুক্তিসঙ্গত ডিফল্ট সংজ্ঞা ব্যবহার করে দেখায় যে তারা যে পদ্ধতিতে প্রেরণ করবে তা সংজ্ঞায়িত না করা হয়। উদাহরণস্বরূপ, len(o)সাধারণত সমাধান হয় o.__len__()তবে যদি সেই পদ্ধতিটি সংজ্ঞায়িত না হয় তবে এটি চেষ্টা করবে iter(o).__len__()

লুপ জন্য একটি মূলত পদ সংজ্ঞায়িত করা হয় next(), iter()এবং আরো অনেক কিছু মৌলিক নিয়ন্ত্রণ কাঠামো। সাধারণভাবে কোড

for i in %EXPR%:
    %LOOP%

এমন কিছুতে আনপ্যাক করা হবে

_a_iter = iter(%EXPR%)
while True:
    try:
        i = next(_a_iter)
    except StopIteration:
        break
    %LOOP%

সুতরাং এই ক্ষেত্রে

for i in a:
    i += 1

প্যাক করা হয়

_a_iter = iter(a) # = a.__iter__()
while True:
    try: 
        i = next(_a_iter) # = _a_iter.__next__()
    except StopIteration:
        break
    i += 1

এর অর্ধেকটি হ'ল i += 1। সাধারণভাবে %ASSIGN% += %EXPR%প্যাক করা হয় %ASSIGN% = %ASSIGN%.__iadd__(%EXPR%)। এখানে স্থান __iadd__(self, other)সংযোজন এবং নিজেই ফিরে আসে।

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

সুতরাং এখানে আপনার কোড মত দেখাচ্ছে

_a_iter = iter(a)
while True:
    try:
        i = next(_a_iter)
    except StopIteration:
        break
    i = iadd(i,1)

যেখানে আমরা সংজ্ঞা দিতে পারি

def iadd(o, v):
    try:
        return o.__iadd__(v)
    except AttributeError:
        return o.__add__(v)

আপনার দ্বিতীয় বিট কোডটিতে আরও কিছুটা চলছে। দুটি নতুন জিনিস আমাদের জানা দরকার তা %ARG%[%KEY%] = %VALUE%হ'ল তা প্যাক করা হয় (%ARG%).__setitem__(%KEY%, %VALUE%)এবং %ARG%[%KEY%]প্যাক করা হয় না (%ARG%).__getitem__(%KEY%)। এই জ্ঞান ফেলে একসঙ্গে আমরা পেতে a[ix] += 1করতে প্যাক না a.__setitem__(ix, a.__getitem__(ix).__add__(1))(আবার __add__বদলে __iadd__কারণ __iadd__আছে ints দ্বারা বাস্তবায়িত হয়নি)। আমাদের চূড়ান্ত কোডটি দেখে মনে হচ্ছে:

_a_iter = iter(enumerate(a))
while True:
    try:
        index, i = next(_a_iter)
    except StopIteration:
        break
    a.__setitem__(index, iadd(a.__getitem__(index), 1))

প্রথমটি কেন দ্বিতীয়টি লিস্টটি সংশোধন করে না এমন বিষয়ে আপনার প্রশ্নের উত্তর দেওয়ার জন্য, আমাদের প্রথম স্নিপেটে আমরা iযেখান থেকে পাচ্ছি next(_a_iter), যার অর্থ iএকটি হবে int। যেহেতু intস্থানে পরিবর্তন করা যায় না, i += 1তাই তালিকায় কিছুই করে না। আমাদের দ্বিতীয় ক্ষেত্রে আমরা আবার পরিবর্তন করছি না intতবে কল করে তালিকার সংশোধন করছি __setitem__

এই পুরো বিস্তৃত অনুশীলনের কারণ হ'ল আমি মনে করি এটি পাইথন সম্পর্কে নিম্নলিখিত পাঠটি শেখায়:

  1. পাইথনের পঠনযোগ্যতার দাম হ'ল এটি যাদুগুলিকে ডাবল স্কোর পদ্ধতিগুলি সর্বদা কল করে।
  2. অতএব, পাইথন কোডের যে কোনও টুকরোটি সত্যই বোঝার সুযোগ পাওয়ার জন্য আপনাকে এই অনুবাদগুলি এর কাজগুলি বুঝতে হবে।

ডাবল আন্ডারস্কোর পদ্ধতিগুলি শুরু করার সময় বাধা, তবে পাইথনের "চলমান সিউডোকোড" খ্যাতিকে সমর্থন করার জন্য এগুলি প্রয়োজনীয়। একটি অদ্ভুত পাইথন প্রোগ্রামারকে এই পদ্ধতিগুলি এবং কীভাবে তারা আহ্বান জানায় সে সম্পর্কে একটি সম্পূর্ণ ধারণা থাকবে এবং যেখানে এটি এটি করার জন্য যথার্থ বোঝায় সেগুলি সংজ্ঞায়িত করবে।

সম্পাদনা করুন : @ দালতাব "সংগ্রহ" শব্দটিটি ব্যবহার করে আমার opালু সংশোধন করেছেন।


2
"পুনরাবৃত্তকারীরা সংগ্রহগুলিও" একেবারেই সঠিক নয়: এগুলি পুনরাবৃত্তও হয় তবে সংগ্রহগুলিও রয়েছে __len__এবং__contains__
ডেল্টাব

2

+=ভিন্নভাবে কিনা বর্তমান মান উপর ভিত্তি করে কাজ চপল বা অপরিবর্তনীয় । পাইথনের বিকাশকারীরা যেহেতু বিভ্রান্তিকর হবে তা ভীত হওয়ায় এটি পাইথনটিতে বাস্তবায়িত হতে দীর্ঘ সময় দেখায় এটিই মূল কারণ ছিল।

যদি iকোনও অন্তর্নির্মিত হয়, তবে এটি পরিবর্তন করা যাবে না যেহেতু ইনটগুলি পরিবর্তনযোগ্য নয় এবং এইভাবে যদি iপরিবর্তনের মান হয় তবে অবশ্যই এটি অন্য কোনও বস্তুর দিকে আবশ্যক:

>>> i=3
>>> id(i)
14336296
>>> i+=1
>>> id(i)
14336272   # Other object

তবে যদি বাম দিকটি পরিবর্তনীয় হয় তবে + = আসলে এটি পরিবর্তন করতে পারে; এটি যদি এটি একটি তালিকা পছন্দ করুন:

>>> i=[]
>>> id(i)
140257231883944
>>> i+=[1]
>>> id(i)
140257231883944  # Still the same object!

আপনার লুপে, ঘুরে ফিরে iপ্রতিটি উপাদান বোঝায় a। যদি সেগুলি পূর্ণসংখ্যা হয়, তবে প্রথম ক্ষেত্রেটি প্রযোজ্য এবং ফলাফলটি i += 1অবশ্যই এটি অন্য কোনও পূর্ণসংখ্যার অবজেক্টকে বোঝায়। aঅবশ্যই তালিকার তালিকায় এখনও সর্বদা একই উপাদান রয়েছে।


আমি চপল এবং অপরিবর্তনীয় বস্তুর মধ্যে এই পার্থক্য বুঝতে পারছি না যদি i = 1সেট iএকটি অপরিবর্তনীয় পূর্ণসংখ্যা বস্তু, তারপর i = []সেট করা উচিত iএকটি অপরিবর্তনীয় তালিকা বস্তু। অন্য কথায়, কেন পূর্ণসংখ্যার অবজেক্টগুলি অপরিবর্তনীয় এবং তালিকাভিত্তিক বস্তুগুলিকে পরিবর্তনীয় করে দেয়? এর পিছনে কোনও যুক্তি দেখছি না।
জর্জিও

@ জর্জিও: বিভিন্ন শ্রেণি থেকে আসা বস্তুগুলি, এমন listপদ্ধতি প্রয়োগ করে যা এর বিষয়বস্তুগুলিকে পরিবর্তন করে, intতা করে না। [] হয় একটি চপল তালিকা বস্তু, এবং i = []দেয় iযে বস্তুর পড়ুন।
রিমকো জারলিচ

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

@ রেমকো জারলিচ: আমি বুঝতে পারি যে বিভিন্ন ক্লাসগুলি আলাদাভাবে আচরণ করে, আমি বুঝতে পারি না কেন তারা এইভাবে প্রয়োগ করা হয়েছিল, অর্থাৎ আমি এই পছন্দটির পিছনে যুক্তি বুঝতে পারি না। আমি +=উভয় প্রকারের জন্য একইরকম আচরণ করতে অপারেটর / পদ্ধতিটি প্রয়োগ করতাম (অন্তত বিস্ময়ের নীতি): হয় মূল বস্তুটি পরিবর্তন করুন বা উভয় পূর্ণসংখ্যার এবং তালিকার জন্য একটি পরিবর্তিত অনুলিপি ফিরিয়ে আনুন।
জর্জিও

1
@ জর্জিও: এটি একেবারে সত্য যা +=পাইথনের ক্ষেত্রে আশ্চর্যজনক, তবে অনুভূত হয়েছিল যে আপনি যে অন্যান্য বিকল্পগুলি উল্লেখ করেছেন তাও অবাক হওয়ার মতো ছিল, বা কমপক্ষে বাস্তবিক (মূল বিষয়টিকে পরিবর্তন করা খুব সাধারণ ধরণের মানের সাথে করা যায় না) আপনি ইনটস + ইনটস, ইন্টস ব্যবহার করেন And এবং পুরো তালিকাটি অনুলিপি করার চেয়ে অনুলিপি করার চেয়ে ব্যয়বহুল, পাইথন তালিকাগুলি এবং অভিধানের মতো বিষয়গুলি পরিষ্কারভাবে না বলা পর্যন্ত অনুলিপি করেন না)। এটি তখন এক বিশাল বিতর্ক ছিল।
রিমকো জারলিচ

1

এখানে লুপ এক প্রকার অপ্রাসঙ্গিক। অনেকগুলি ফাংশন প্যারামিটার বা আর্গুমেন্টের মতো, এর মতো লুপের জন্য একটি সেট আপ করা মূলত কেবল অভিনব দেখানোর জন্য অ্যাসাইনমেন্ট।

পূর্ণসংখ্যা পরিবর্তনযোগ্য। তাদের সংশোধন করার একমাত্র উপায় হ'ল একটি নতুন পূর্ণসংখ্যা তৈরি করা এবং এটিকে আসল নামে একই নামকরণ করা।

অ্যাসাইনমেন্ট ম্যাপের জন্য পাইথনের শব্দার্থবিদ্যা সরাসরি সি এর (সিপাইনের পাইওবজেক্ট * পয়েন্টারগুলিকে অবাক করা নয়) সহ কেবলমাত্র সতর্কতা হ'ল সবকিছুই পয়েন্টার, এবং আপনাকে ডাবল পয়েন্টার রাখার অনুমতি নেই। নিম্নলিখিত কোড বিবেচনা করুন:

a = 1
b = a
b += 1
print(a)

কি ঘটেছে? এটা তোলে ছাপে 1। কেন? এটি আসলে নীচের সি কোডের সমতুল্য:

i64* a = malloc(sizeof(i64));
*a = 1;
i64* b = a;
i64* tmp = malloc(sizeof(i64));
tmp = *b + 1;
b = tmp;
printf("%d\n", *a);

সি কোডে, এটি সুস্পষ্ট যে এর মান aসম্পূর্ণরূপে ক্ষতিগ্রস্থ নয়।

তালিকাগুলি কেন কাজ করে বলে মনে হচ্ছে, উত্তরটি কেবলমাত্র আপনি একই নামে অর্পণ করছেন। তালিকাগুলি পরিবর্তনযোগ্য। নামের বস্তুর পরিচয় a[0]পরিবর্তন হবে তবে a[0]এটি একটি বৈধ নাম। আপনি নিম্নলিখিত কোড সহ এটি পরীক্ষা করতে পারেন:

x = 1
a = [x]
print(a[0] is x)
a[0] += 1
print(a[0] is x)

তবে, এটি তালিকার জন্য বিশেষ নয়। সেই কোডটির a[0]সাথে প্রতিস্থাপন করুন yএবং আপনি ঠিক একই ফলাফল পাবেন।

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