"সর্বনিম্ন বিস্ময়" এবং মিউটেবল ডিফল্ট তর্ক


2593

পাইথনের সাথে দীর্ঘকালীন যে কেউ ঝুঁকছেন তাকে নিম্নলিখিত ইস্যু দ্বারা কামড় (বা টুকরো টুকরো টুকরো) করা হয়েছে:

def foo(a=[]):
    a.append(5)
    return a

পাইথন অনভিজ্ঞ এই ফাংশন সবসময় শুধুমাত্র একটি উপাদান সঙ্গে একটি তালিকা ফিরতে আশা: [5]। পরিবর্তে ফলাফলটি খুব আলাদা এবং খুব আশ্চর্যজনক (এক নবজাতকের জন্য):

>>> foo()
[5]
>>> foo()
[5, 5]
>>> foo()
[5, 5, 5]
>>> foo()
[5, 5, 5, 5]
>>> foo()

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

সম্পাদনা করুন :

বাজাক একটি আকর্ষণীয় উদাহরণ তৈরি করেছেন। আপনার বেশিরভাগ মন্তব্য এবং বিশেষত উত্সালের সাথে একসাথে আমি আরও বিশদভাবে জানিয়েছি:

>>> def a():
...     print("a executed")
...     return []
... 
>>>            
>>> def b(x=a()):
...     x.append(5)
...     print(x)
... 
a executed
>>> b()
[5]
>>> b()
[5, 5]

আমার কাছে, মনে হয় ডিজাইনের সিদ্ধান্তটি যেখানে প্যারামিটারগুলির সুযোগ রাখবে তার সাথে সম্পর্কিত: ফাংশনের ভিতরে বা এটির সাথে "একসাথে"?

ফাংশনের অভ্যন্তরে বাইন্ডিং করার অর্থ xহ'ল কার্যকরভাবে নির্দিষ্ট ডিফল্টের সাথে আবদ্ধ যখন ফাংশন বলা হয়, সংজ্ঞায়িত হয় না এমন কিছু যা গভীর ত্রুটি উপস্থাপন করে: defলাইনটি "হাইব্রিড" অর্থে যে বাঁধাইয়ের অংশটি হবে (এর ফাংশন অবজেক্ট) সংজ্ঞা, এবং অংশ (ডিফল্ট পরামিতিগুলির অ্যাসাইনমেন্ট) এ ফাংশন অনুরোধের সময় ঘটবে।

আসল আচরণটি আরও সামঞ্জস্যপূর্ণ: ফাংশন সংজ্ঞা অনুসারে, সেই লাইনটি কার্যকর করা হলে সেই লাইনের সমস্ত কিছুই মূল্যায়িত হয়।



4
আমি পরিবর্তনীয় যুক্তিগুলিতে কোনও গড়পড়তা ব্যক্তির পক্ষে কমপক্ষে বিস্মিত নীতি লঙ্ঘন করার বিষয়ে সন্দেহ করি না এবং আমি সেখানে নতুনদের পদক্ষেপ নিতে দেখেছি, তারপরে বৌদ্ধিকভাবে মেইলিং তালিকার পরিবর্তে মেলিং টিপলগুলি প্রতিস্থাপন করেছি। তবুও পরিবর্তনীয় যুক্তিগুলি পাইথন জেন (পেপ 20) এর সাথে সামঞ্জস্যপূর্ণ এবং "ডাচদের পক্ষে স্পষ্ট" (হার্ড কোর পাইথন প্রোগ্রামারদের দ্বারা বোঝা / শোষণ করা) ধারার মধ্যে পড়ে। ডক স্ট্রিং সহ প্রস্তাবিত ওয়ার্কআরউন্ডটি সর্বোত্তম, তবুও ডক স্ট্রিংগুলির সাথে প্রতিরোধ এবং কোনও (লিখিত) ডক্স আজকাল এতটা অস্বাভাবিক নয়। ব্যক্তিগতভাবে, আমি একটি সাজসজ্জার পছন্দ করতে চাই (@ ফিক্সড_ডিফল্টস বলুন)।
সার্জ 16

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

12
"পাইথন নোভিসেস এই ফাংশনটি সর্বদা কেবলমাত্র একটি উপাদান দিয়ে একটি তালিকা প্রত্যাশা করবে বলে আশা করবে [5]।" আমি একজন পাইথন নবাগত, এবং আমি এটির আশা করব না, কারণ স্পষ্টতই foo([1])ফিরে আসবে [1, 5], না [5]। আপনি যা বলতে চেয়েছেন তা হ'ল কোনও নবজাতক আশা করে যে কোনও প্যারামিটার না দিয়ে ডাকা ফাংশনটি সর্বদা ফিরে আসবে [5]
সিম্পিক্টোমোরফিক

2
এই প্রশ্নটি জিজ্ঞাসা করে "কেন এটি [ভুল পথে] এত বাস্তবায়িত হয়েছিল?" এটি জিজ্ঞাসা করে না "সঠিক উপায় কী?" , যা দ্বারা আচ্ছাদিত রয়েছে [ কেন আরগ = ব্যবহার করে পাইথনের কোনও পরিবর্তনযোগ্য ডিফল্ট যুক্তি ইস্যুটি স্থির করে না? ] * ( স্ট্যাকওভারফ্লো / প্রশ্নগুলি 1067676729/… )। নতুন ব্যবহারকারীরা প্রায় সবসময়ই প্রাক্তনটির প্রতি কম আগ্রহী এবং পরবর্তীকালে অনেক বেশি, তাই এটি কখনও কখনও উদ্ধৃত করার জন্য খুব দরকারী লিঙ্ক / ডুপে।
স্মিচ

উত্তর:


1612

আসলে, এটি কোনও ডিজাইনের ত্রুটি নয় এবং এটি অভ্যন্তরীণ বা পারফরম্যান্সের কারণে নয়।
এটি পাইথন থেকে ফাংশনগুলি প্রথম শ্রেণীর অবজেক্ট এবং কেবল কোনও টুকরো কোড নয় এটি থেকে আসে।

যত তাড়াতাড়ি আপনি এইভাবে চিন্তা করতে পারেন, তারপরে এটি সম্পূর্ণরূপে বোধগম্য হয়: একটি ফাংশন একটি সংজ্ঞা যা তার সংজ্ঞা অনুসারে মূল্যায়ন করা হয়; ডিফল্ট প্যারামিটারগুলি "সদস্যের ডেটা" ধরণের এবং তাই তাদের অবস্থা এক কল থেকে অন্য কলটিতে পরিবর্তিত হতে পারে - ঠিক অন্য কোনও বস্তুর মতো।

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


80
উপরের উত্তরটি যে কারও কাছে পড়ার জন্য, আমি দৃ strongly়ভাবে আপনাকে লিঙ্কড এফবোট নিবন্ধের মাধ্যমে পড়ার জন্য সময় দেওয়ার পরামর্শ দিচ্ছি। পাশাপাশি অন্যান্য সমস্ত দরকারী তথ্য হিসাবে, এই ভাষা বৈশিষ্ট্যটি কীভাবে ফলাফলের ক্যাচিং / স্মৃতিচারণের জন্য ব্যবহার করা যেতে পারে তার অংশটি খুব সহজেই জানা যায়!
ক্যাম জ্যাকসন

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

312
দুঃখিত, তবে "পাইথনের বৃহত্তম ডাব্লুটিএফ" হিসাবে বিবেচিত কিছু হ'ল অবশ্যই ডিজাইনের ত্রুটি । এটি এক পর্যায়ে প্রত্যেকের জন্য বাগের উত্স , কারণ কেউ প্রথমে আচরণটি প্রত্যাশা করে না - যার অর্থ এটি শুরু করার মতো নকশা করা উচিত হয়নি। তাদের কোন হুপসের মধ্য দিয়ে ঝাঁপিয়ে পড়তে হবে তা আমি চিন্তা করি না, তাদের পাইথনটি তৈরি করা উচিত ছিল যাতে ডিফল্ট যুক্তি স্থির থাকে non
ব্লুরাজা - ড্যানি পিফ্লুঘুফুট

192
এটি কোনও ডিজাইনের ত্রুটি হোক বা না হোক, আপনার উত্তরটি বোঝা যাচ্ছে যে এই আচরণটি কোনওভাবেই প্রয়োজনীয়, প্রাকৃতিক এবং সুস্পষ্ট যে প্রদত্ত ফাংশনগুলি প্রথম শ্রেণির অবজেক্টস, এবং এটি কেবল তেমনটি নয়। পাইথনের ক্লোজার রয়েছে। আপনি যদি ফাংশনের প্রথম লাইনে কোনও অ্যাসাইনমেন্টের সাথে ডিফল্ট যুক্তিটি প্রতিস্থাপন করেন তবে এটি প্রতিটি কল (সংলগ্ন সুযোগে ঘোষিত নামগুলি ব্যবহার করে) সম্ভবত অভিব্যক্তিটির মূল্যায়ন করে। কোনও কারণ নেই যে ফাংশনটি ঠিক একইভাবে ডাকা হলে প্রতিবার ডিফল্ট যুক্তিগুলি মূল্যায়ন করা সম্ভব বা যুক্তিসঙ্গত হবে না।
মার্ক অ্যামেরি

24
ডিজাইনটি সরাসরি অনুসরণ করে না functions are objects। আপনার দৃষ্টান্তে, প্রস্তাবটি হ'ল বৈশিষ্ট্যের চেয়ে বৈশিষ্ট্য হিসাবে ফাংশনগুলির ডিফল্ট মানগুলি প্রয়োগ করতে হবে।
bukzor

273

মনে করুন আপনার নীচের কোড রয়েছে

fruits = ("apples", "bananas", "loganberries")

def eat(food=fruits):
    ...

আমি যখন খাওয়ার ঘোষণা দেখি, তখন সবচেয়ে কম অবাক করা বিষয়টি মনে করা হয় যে যদি প্রথম প্যারামিটারটি দেওয়া না হয় তবে এটি টিউপের সমান হবে will ("apples", "bananas", "loganberries")

যাইহোক, কোড পরে অনুমিত, আমি যেমন কিছু না

def some_random_function():
    global fruits
    fruits = ("blueberries", "mangos")

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

আসল সমস্যাটি পরিবর্তনীয় পরিবর্তনশীলগুলির সাথে সম্পর্কিত এবং সমস্ত ভাষায় কিছুটা হলেও এই সমস্যা থাকে। এখানে একটি প্রশ্ন রয়েছে: ধরুন জাভাতে আমার কাছে নিম্নলিখিত কোড রয়েছে:

StringBuffer s = new StringBuffer("Hello World!");
Map<StringBuffer,Integer> counts = new HashMap<StringBuffer,Integer>();
counts.put(s, 5);
s.append("!!!!");
System.out.println( counts.get(s) );  // does this work?

এখন, আমার মানচিত্রটি StringBufferযখন মানচিত্রটিতে স্থাপন করা হয়েছিল তখন কীটির মান ব্যবহার করে বা কীটি রেফারেন্স দ্বারা সঞ্চয় করে? যেভাবেই হোক, কেউ অবাক; হয় যে ব্যক্তি যার সাথে এটি স্থাপন করেছিল তার Mapঅনুরূপ একটি মান ব্যবহার করে বস্তুটি বের করার চেষ্টা করেছিল বা যে ব্যক্তি যে জিনিসটি ব্যবহার করছে তারা আক্ষরিকভাবে একই বস্তু থাকা সত্ত্বেও যে ব্যক্তি তাদের অবজেক্টটি পুনরুদ্ধার করতে পারে না বলে মনে হয় এটি মানচিত্রে রাখার জন্য ব্যবহৃত হয়েছিল (এটি আসলে পাইথন তার মিউটিটেবল অন্তর্নির্মিত ডেটা অভিধানের কী হিসাবে ব্যবহার করার অনুমতি দেয় না)।

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

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


30
আমি মনে করি এটি বিতর্কের বিষয়। আপনি একটি গ্লোবাল ভেরিয়েবলের সাথে অভিনয় করছেন। আপনার বৈশ্বিক চলক জড়িত কোডে আপনার কোথাও যে কোনও মূল্যায়ন করা এখন (সঠিকভাবে) উল্লেখ করা হবে ("ব্লুবেরি", "ম্যাঙ্গোস")। ডিফল্ট প্যারামিটারটি অন্য কোনও মামলার মতো হতে পারে।
স্টিফানো বোরিনি

47
আসলে, আমি মনে করি না যে আমি আপনার প্রথম উদাহরণের সাথে একমত। আমি নিশ্চিত নই যে প্রথম দিকের মতো একটি ইনিশিয়ালাইজারটি পরিবর্তন করার ধারণাটি আমি পছন্দ করি তবে আমি যদি তা করি তবে আমি এটির বর্ণনাকালীন আচরণ করার প্রত্যাশা করব - ডিফল্ট মানটি পরিবর্তিত করে ("blueberries", "mangos")
বেন ফাঁকা 18

12
ডিফল্ট প্যারামিটার হয় অন্য কোন ক্ষেত্রে মত। যা অপ্রত্যাশিত তা হ'ল প্যারামিটারটি বিশ্বব্যাপী পরিবর্তনশীল, এবং স্থানীয় নয়। যার পালা কারণ কোডটি ফাংশন সংজ্ঞায় কার্যকর করা হয়, কল নয়। একবার আপনি এটি পেলে এবং ক্লাসগুলির জন্য একই হয়, এটি পুরোপুরি স্পষ্ট।
লেনার্ট রেগেব্রো

17
আমি উজ্জ্বল না হয়ে বিভ্রান্তিকর উদাহরণটি পেয়েছি। যদি some_random_function()আপনার স্বাক্ষরে করার fruitsপরিবর্তে এটি বরাদ্দ এর আচরণ eat() করবে পরিবর্তন করুন। বর্তমানের দুর্দান্ত ডিজাইনের জন্য অনেক কিছুই। আপনি যদি অন্য কোনও ডিফল্ট যুক্তি ব্যবহার করেন যা অন্য কোথাও উল্লেখ করা হয় এবং তারপরে ফাংশনের বাইরে থেকে রেফারেন্সটি সংশোধন করে থাকেন, আপনি সমস্যার জন্য বলছেন। আসল ডাব্লুটিএফ হ'ল লোকেরা যখন নতুন করে ডিফল্ট আর্গুমেন্ট (একটি তালিকার আক্ষরিক বা কোনও নির্মাণকারীর কাছে একটি কল) সংজ্ঞায়িত করে এবং তখনও কিছুটা বিস্তৃত হয়।
অ্যালেক্সিস

13
আপনি কেবল স্পষ্টভাবে globalটিপলটিকে ঘোষণা করেছেন এবং পুনরায় নিয়োগ দিয়েছেন - এর eatপরে আলাদাভাবে কাজ করলে অবাক হওয়ার কিছু নেই ।
ব্যবহারকারী 3467349

241

ডকুমেন্টেশনের প্রাসঙ্গিক অংশ :

ফাংশন সংজ্ঞা কার্যকর করা হলে ডিফল্ট প্যারামিটার মানগুলি বাম থেকে ডানে মূল্যায়ন করা হয়। এর অর্থ হ'ল অভিব্যক্তিটি একবার মূল্যায়ন করা হয়, যখন ফাংশনটি সংজ্ঞায়িত হয় এবং প্রতিটি কলের জন্য একই "প্রাক-গণিত" মান ব্যবহৃত হয়। ডিফল্ট প্যারামিটারটি কোনও পরিবর্তনযোগ্য অবজেক্ট, যেমন একটি তালিকা বা অভিধান হিসাবে যখন এটি বুঝতে গুরুত্বপূর্ণ হয়: যদি ফাংশনটি বস্তুটি সংশোধন করে (উদাহরণস্বরূপ কোনও তালিকায় কোনও আইটেম যুক্ত করে), ডিফল্ট মান কার্যকর হয়। এটি সাধারণত যা ছিল তা নয়। এর চারপাশের উপায়টি হ'ল Noneডিফল্ট হিসাবে ব্যবহার করা এবং ফাংশনটির শরীরে স্পষ্টভাবে এটি পরীক্ষা করা, উদাহরণস্বরূপ:

def whats_on_the_telly(penguin=None):
    if penguin is None:
        penguin = []
    penguin.append("property of the zoo")
    return penguin

180
"এটি সাধারণত যা উদ্দেশ্য ছিল তা নয়" এবং "এর চারপাশের উপায়" এই বাক্যাংশগুলি যেমন তারা কোনও ডিজাইনের ত্রুটি দলিল করছে're
bukzor

4
@ ম্যাথুজ: আমি ভাল করেই অবগত, তবে এটার ক্ষতি হওয়ার মতো নয়। আপনি সাধারণত স্টাইল গাইড এবং লিন্টারগুলি শর্তহীন পরিবর্তিত ডিফল্ট মানগুলিকে এই কারণে ভুল হিসাবে পতাকাঙ্কিত করতে দেখবেন। একই জিনিসটি করার সুস্পষ্ট উপায় হ'ল ফাংশনটিতে কোনও গুণকে স্টাফ করা ( function.data = []) বা আরও ভাল, কোনও বস্তু তৈরি করা।
বুকজোর

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

33
"এটি সাধারণত যা ইচ্ছা ছিল তা নয়" এই বাক্যটির অর্থ "প্রোগ্রামার আসলে যা ঘটতে চেয়েছিল তা নয়," পাইথন যা করার কথা বলেছিল তা নয় "।
হোল্ডেনওয়েব

4
@ হোল্ডেনওয়েব বাহ, আমি পার্টিতে মেগা-দেরী করেছি। প্রসঙ্গটি প্রদত্ত, বুকজার পুরোপুরি সঠিক: তারা এমন আচরণ / ফলাফলটি নথিভুক্ত করছে যা তারা যখন সিদ্ধান্ত নিয়েছিল তখন ভাষাটি ফাংশনটির সংজ্ঞা কার্যকর করতে হবে। যেহেতু এটি তাদের নকশা নির্বাচনের একটি অনিচ্ছাকৃত ফলাফল, এটি একটি ডিজাইনের ত্রুটি। যদি এটি কোনও ডিজাইনের ত্রুটি না হয় তবে "এটির কাছাকাছি উপায়" দেওয়ারও দরকার পড়েনি।
কোড_ড্রেড

118

পাইথন ইন্টারপ্রেটার অভ্যন্তরীণ কাজকর্ম সম্পর্কে আমি কিছুই জানি না (এবং আমি সংকলক এবং দোভাষীদেরও বিশেষজ্ঞ নই) তাই আমি যদি অবেদনযোগ্য বা অসম্ভব কিছু প্রস্তাব করি তবে আমাকে দোষ দেবেন না।

প্রদত্ত যে পাইথন অবজেক্টগুলি পারস্পরিক পরিবর্তনযোগ্য আমি মনে করি ডিফল্ট আর্গুমেন্ট স্টাফ ডিজাইন করার সময় এটি বিবেচনায় নেওয়া উচিত। আপনি যখন কোনও তালিকা ইনস্ট্যান্ট করবেন:

a = []

আপনি রেফারেন্সযুক্ত একটি নতুন তালিকা পেতে আশা করি a

কেন a=[]

def x(a=[]):

ফাংশন সংজ্ঞা উপর একটি নতুন তালিকা ইনস্ট্যানটেশন এবং অনুরোধ নয়? এটি ঠিক যেমন আপনি জিজ্ঞাসা করছেন "যদি ব্যবহারকারী যুক্তি না দেয় তবে একটি নতুন তালিকা ইনস্ট্যান্টিয়েট করুন এবং এটি ব্যবহারকারীর দ্বারা তৈরি করা হয়েছে এমনভাবে ব্যবহার করুন"। আমি মনে করি এটি পরিবর্তে অস্পষ্ট:

def x(a=datetime.datetime.now()):

ব্যবহারকারী, aআপনি সংজ্ঞায়িত বা সম্পাদনকালে ডেটটাইমের সাথে কি ডিফল্ট করতে চান x? এই ক্ষেত্রে, আগেরটির মতো, আমি একই আচরণটি রাখব যেন ডিফল্ট আর্গুমেন্ট "অ্যাসাইনমেন্ট" ফাংশনের প্রথম নির্দেশ ( datetime.now()ফাংশন অনুরোধের নামে ডাকা হয়)। অন্যদিকে, ব্যবহারকারী যদি সংজ্ঞা-সময়ের ম্যাপিং চান তবে তিনি লিখতে পারেন:

b = datetime.datetime.now()
def x(a=b):

আমি জানি, আমি জানি: এটি একটি বন্ধ। বিকল্পভাবে পাইথন সংজ্ঞা-সময় বন্ধন জোর করার জন্য একটি কীওয়ার্ড সরবরাহ করতে পারে:

def x(static a=b):

11
আপনি এটি করতে পারেন: ডিএফ এক্স (এ = কিছুই নয়): এবং তারপরে, যদি কেউ না হয় তবে একটি = ডেটটাইম.ডেটটাইম.নো () নির্ধারণ করুন
আনন

20
এই জন্য আপনাকে ধন্যবাদ. আমি কেন সত্যিই আমার আঙুল রাখতে পারি না কেন এটি আমাকে শেষ করে দেয়। আপনি এটি ন্যূনতম ঝলক এবং বিভ্রান্তির সাথে সুন্দরভাবে করেছেন done যেহেতু কেউ সি ++ তে সিস্টেম প্রোগ্রামিং থেকে আগত এবং কখনও কখনও নির্লজ্জভাবে ভাষা বৈশিষ্ট্যগুলিকে "অনুবাদ" করে, এই মিথ্যা বন্ধুটি আমাকে ক্লাসের বৈশিষ্ট্যের মতোই মাথার নরম সময়ে লাথি মেরেছিল। আমি বুঝতে পারি যে জিনিসগুলি কেন এইরকম, তবে আমি এটির সাহায্য না করে অপছন্দ করতে পারি না, তা যাই হোক না কেন ইতিবাচক কারণেই আসুক। কমপক্ষে এটি আমার অভিজ্ঞতার বিপরীত, আমি সম্ভবত (আশা করি) এটি কখনই ভুলতে পারি না ...
AndreasT

5
@ আন্দ্রেস একবার আপনি পাইথনকে দীর্ঘ সময়ের জন্য ব্যবহার করলে, আপনি দেখতে পাচ্ছেন যে পাইথনের পক্ষে বিষয়গুলি শ্রেণীর বৈশিষ্ট্য হিসাবে ব্যাখ্যা করা কতটা যৌক্তিক - এটি কেবলমাত্র সি ++ (এবং জাভা, এবং জাভা ইত্যাদির মতো ভাষার সুনির্দিষ্ট এবং সীমাবদ্ধতার কারণে and সি # ...) যে এটি class {}ব্লকের বিষয়বস্তুগুলিকে উদাহরণগুলির সাথে সম্পর্কিত হিসাবে ব্যাখ্যা করার জন্য কোনও অর্থবোধ করে না :) তবে ক্লাসগুলি যখন প্রথম শ্রেণীর অবজেক্ট হয় তবে স্পষ্টতই প্রাকৃতিক জিনিসটি তাদের বিষয়বস্তুগুলি (স্মৃতিতে) প্রতিফলিত করে (কোডে)
কার্ল নচেটেল

6
প্রাকৃতিক কাঠামো আমার বইয়ে কোন তাত্পর্য বা সীমাবদ্ধতা নয়। আমি জানি এটি আনাড়ি এবং কুরুচিপূর্ণ হতে পারে তবে আপনি এটিকে কোনও কিছুর "সংজ্ঞা" বলতে পারেন। গতিশীল ভাষাগুলি আমার কাছে কিছুটা নৈরাজ্যবাদীদের মতো মনে হচ্ছে: নিশ্চয়ই সবাই মুক্ত, তবে কাউকে আবর্জনা খালি করতে এবং রাস্তা প্রশস্ত করার জন্য আপনার কাঠামো দরকার। অনুমান করুন আমি বৃদ্ধ ... :)
অ্যান্ড্রিসট

4
কাজ সংজ্ঞাটি মডিউল লোড সময়ে কার্যকর করা হয়। ফাংশন বডি ফাংশন কল সময়ে কার্যকর করা হয়। ডিফল্ট আর্গুমেন্টটি ফাংশন বডি নয়, ফাংশন সংজ্ঞার অংশ of (নেস্টেড ক্রিয়াকলাপগুলির জন্য এটি আরও জটিল হয়ে
ওঠে

84

ওয়েল, কারণটি বেশ সহজভাবেই বোঝা যায় যে কোড কার্যকর করার সময় বাইন্ডিংগুলি করা হয়, এবং ফাংশন সংজ্ঞা কার্যকর করা হয়, ভাল ... যখন ফাংশনগুলি সংজ্ঞায়িত করা হয়।

এর সাথে তুলনা করুন:

class BananaBunch:
    bananas = []

    def addBanana(self, banana):
        self.bananas.append(banana)

এই কোডটি একই একই অপ্রত্যাশিত ঘটনায় ভোগে suff কলা একটি শ্রেণি বৈশিষ্ট্য, এবং সেইজন্য, আপনি যখন এতে জিনিস যুক্ত করেন, তখন এটি শ্রেণীর সমস্ত উদাহরণে যুক্ত হয়। কারণ ঠিক একই।

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

হ্যাঁ, এটি অপ্রত্যাশিত। কিন্তু একবার পেনি নামলে, পাইথন কীভাবে সাধারণভাবে কাজ করে তা এটি পুরোপুরি ফিট করে। প্রকৃতপক্ষে, এটি একটি ভাল শিক্ষণ সহায়তা এবং এটি কেন হয় তা বুঝতে পারলে আপনি অজগরটিকে আরও ভাল করে তোলেন।

এটি বলেছিল যে এটি কোনও ভাল পাইথন টিউটোরিয়ালে বিশিষ্টভাবে বৈশিষ্ট্যযুক্ত হওয়া উচিত। কারণ যেমন আপনি উল্লেখ করেছেন, প্রত্যেকেই এই সমস্যাটি শীঘ্রই বা পরে চালাবে।


শ্রেণীর প্রতিটি বৈশিষ্ট্যের জন্য পৃথক পৃথক শ্রেণীর বৈশিষ্ট্যটি আপনি কীভাবে সংজ্ঞায়িত করবেন?
কিয়েভেলি

19
এটি প্রতিটি উদাহরণের জন্য পৃথক হলে এটি কোনও শ্রেণির বৈশিষ্ট্য নয়। শ্রেণীর বৈশিষ্ট্যগুলি ক্লাসে থাকা বৈশিষ্ট্য। অত: পর নামটা. সুতরাং তারা সমস্ত দৃষ্টান্তের জন্য একই।
লেনার্ট রেগেব্রো 19

1
আপনি কোন শ্রেণীর একটি বৈশিষ্ট্যকে কীভাবে সংজ্ঞায়িত করেন যা শ্রেণীর প্রতিটি উদাহরণের জন্য আলাদা? (যারা পাইথনের নামকরণ কনভেনশনের সাথে পরিচিত নন এমন কোনও ব্যক্তি কোনও শ্রেণীর সাধারণ সদস্য ভেরিয়েবল সম্পর্কে জিজ্ঞাসা করতে পারেন তা নির্ধারণ করতে পারেনি তাদের জন্য পুনরায় সংজ্ঞায়িত)।
কিয়েভেলি

@ কিভিয়েলি: আপনি একটি শ্রেণির সাধারণ সদস্য ভেরিয়েবল সম্পর্কে কথা বলছেন। :-) আপনি যেকোন পদ্ধতিতে self.attribute = মান বলে উদাহরণের বৈশিষ্ট্যগুলি সংজ্ঞায়িত করেন। উদাহরণস্বরূপ __init __ ()।
লেনার্ট রেজেব্রো 14

@ কিভেলি: দুটি উত্তর: আপনি পারবেন না, কারণ শ্রেণি পর্যায়ে আপনি যে কোনও জিনিস নির্ধারণ করবেন এটি একটি শ্রেণির বৈশিষ্ট্য হবে এবং যে বৈশিষ্ট্যটি অ্যাক্সেস করে সেগুলি একই শ্রেণীর বৈশিষ্ট্যটিতে অ্যাক্সেস করবে; আপনি ব্যবহার করতে পারেন / সাজানোর /, property- যা আসলে শ্রেণিবদ্ধ ফাংশন যা সাধারণ বৈশিষ্ট্যগুলির মতো কাজ করে তবে বৈশিষ্ট্যটিকে ক্লাসের পরিবর্তে সংরক্ষণ করে ( self.attribute = valueলেনার্ট যেমনটি বলেছিলেন তেমন ব্যবহার করে )।
ইথান ফুরম্যান

66

আপনি কেন আত্মনিয়ন্ত্রণ করবেন না?

আমি সত্যিই আশ্চর্য হয়েছি যে পাইথন ( 2এবং।) দ্বারা প্রদত্ত অন্তর্দৃষ্টিপূর্ণ অন্তর্মুখিটি কেউই সম্পাদন করে না3 কলযোগ্যগুলিতে প্রয়োগ) করেন নি।

একটি সাধারণ ছোট্ট ফাংশন funcহিসাবে সংজ্ঞায়িত করা হয়েছে:

>>> def func(a = []):
...    a.append(5)

পাইথন এটির মুখোমুখি হলে, প্রথম কাজটি এটি করবে এটি codeএই ফাংশনটির জন্য একটি অবজেক্ট তৈরি করার জন্য এটি সংকলন করে । এই সংকলন পদক্ষেপটি সমাপ্ত করা হয়, তখন পাইথন মূল্যায়ণ * এবং তারপর সঞ্চয় করে (একটি খালি তালিকা ডিফল্ট আর্গুমেন্ট []এখানে) ফাংশন বস্তুর নিজেই । শীর্ষস্থানীয় উত্তরের হিসাবে উল্লেখ করা হয়েছে: তালিকাকে aএখন ফাংশনের সদস্য হিসাবে বিবেচনা করা যেতে পারেfunc

সুতরাং, আসুন কিছু ফাংশন অবজেক্টের ভিতরে কীভাবে তালিকাটি প্রসারিত হয় তা পরীক্ষা করার আগে এবং পরে কিছুটা অন্তঃসীক্ষণ করি । আমি এটির Python 3.xজন্য ব্যবহার করছি , পাইথন 2 এর জন্যও একই প্রযোজ্য (ব্যবহার করুন __defaults__বা ব্যবহার করুনfunc_defaults পাইথন 2 এ; হ্যাঁ, একই জিনিসটির জন্য দুটি নাম)।

মৃত্যুদন্ড কার্যকর করার আগে কাজ:

>>> def func(a = []):
...     a.append(5)
...     

পাইথন এই সংজ্ঞাটি কার্যকর করার পরে এটি কোনও ডিফল্ট প্যারামিটার নির্দিষ্ট করে ( a = []এখানে) নেবে __defaults__এবং ফাংশন অবজেক্টের জন্য এট্রিবিউটে এগুলি ক্র্যাম করবে (প্রাসঙ্গিক বিভাগ: কলযোগ্য):

>>> func.__defaults__
([],)

ঠিক আছে, সুতরাং একক এন্ট্রি হিসাবে খালি তালিকা __defaults__যেমন প্রত্যাশিত।

ফাঁসি কার্যকর করার পরে:

আসুন এখন এই ফাংশনটি সম্পাদন করা যাক:

>>> func()

এখন, এগুলি __defaults__আবার দেখুন:

>>> func.__defaults__
([5],)

বিস্মিত? অবজেক্টের ভিতরে মান বদলে যায়! ক্রিয়াকলাপের ধারাবাহিক কলগুলি এখন কেবল সেই এমবেড করা listঅবজেক্টে যুক্ত হবে:

>>> func(); func(); func()
>>> func.__defaults__
([5, 5, 5, 5],)

সুতরাং, সেখানে আপনার এটি রয়েছে, কারণ এই 'ত্রুটি' ঘটে যাওয়ার কারণ হ'ল ডিফল্ট যুক্তিগুলি ফাংশন অবজেক্টের অংশ। এখানে অদ্ভুত কিছু চলছে না, এটি কেবল কিছুটা অবাক করে দেওয়া।

এটির লড়াইয়ের সাধারণ সমাধানটি হ'ল ব্যবহার None ডিফল্ট হিসাবে করা এবং তারপরে ফাংশন বডিটিতে আরম্ভ করা:

def func(a = None):
    # or: a = [] if a is None else a
    if a is None:
        a = []

যেহেতু ফাংশন বডিটি প্রতিবারই নতুনভাবে কার্যকর করা হয়, তাই কোনও যুক্তি পাস না করা হলে আপনি সর্বদা একটি নতুন নতুন খালি তালিকা পান a


তালিকায়িত __defaults__ফাংশনে ব্যবহৃত একইরূপটি যাচাই funcকরতে আপনি কেবলমাত্র ফাংশনটির দেহের অভ্যন্তরে ব্যবহৃত idতালিকার ফিরে আসতে আপনার ফাংশন পরিবর্তন করতে পারেন a। তারপরে, এটিকে তালিকার সাথে __defaults__(অবস্থানের [0]মধ্যে) তুলনা করুন__defaults__ ) এবং কিভাবে আপনি এই প্রকৃতপক্ষে একই তালিকা উদাহরণস্বরূপ উল্লেখ করা হয় দেখতে পাবেন:

>>> def func(a = []): 
...     a.append(5)
...     return id(a)
>>>
>>> id(func.__defaults__[0]) == func()
True

আত্মরক্ষার শক্তি দিয়ে সব!


* যে পাইথন ফাংশন সংকলন করার সময় ডিফল্ট আর্গুমেন্ট মূল্যায়ন যাচাই করতে, নিম্নলিখিত নির্বাহ চেষ্টা করুন:

def bar(a=input('Did you just see me without calling the function?')): 
    pass  # use raw_input in Py2

যেমন আপনি লক্ষ্য করবেন, input()ফাংশনটি তৈরির প্রক্রিয়া করার আগে এবং নামটিকে আবদ্ধ করার আগে তাকে ডাকা barহয়।


1
হয় id(...)যে গত যাচাইয়ের জন্য প্রয়োজন, বা হবে isঅপারেটর একই প্রশ্নের উত্তর?
দাস-জি

1
@ ড্যাস-জি isঠিকঠাক করবে, আমি কেবল ব্যবহার করেছি id(val)কারণ আমি মনে করি এটি আরও স্বজ্ঞাত হতে পারে।
দিমিত্রিস ফাসারাকিস হিলিয়ার্ড

Noneডিফল্ট হিসাবে ব্যবহার করা আত্মবিজ্ঞানের কার্যকারিতা কঠোরভাবে সীমাবদ্ধ করে __defaults__, তাই আমি মনে করি না যে এটি __defaults__কাজ করে এমনভাবে কাজ করার একটি প্রতিরক্ষা হিসাবে কাজ করে। অলস-মূল্যায়ন উভয় পক্ষ থেকে ফাংশন ডিফল্টকে দরকারী রাখতে আরও কাজ করবে।
ব্রিলিয়ান্ড

58

আমি ভাবতাম যে রানটাইমে অবজেক্ট তৈরি করা আরও ভাল পন্থা হবে। আমি এখনই কম নিশ্চিত, যেহেতু আপনি কিছু দরকারী বৈশিষ্ট্য হারাচ্ছেন, যদিও নবাগত বিভ্রান্তি রোধে এটি নির্বিশেষে এটি মূল্যবান হতে পারে। এটি করার অসুবিধাগুলি হ'ল:

1. কর্মক্ষমতা

def foo(arg=something_expensive_to_compute())):
    ...

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

2. জোর করে আবদ্ধ পরামিতি

ল্যাম্বদা তৈরি হওয়ার সময় একটি ল্যাম্বডাটির প্যারামিটারগুলিকে চলকের বর্তমান বাঁধার সাথে বাঁধাই একটি দরকারী কৌশল । উদাহরণ স্বরূপ:

funcs = [ lambda i=i: i for i in range(10)]

এটি যথাক্রমে 0,1,2,3 ... ফেরত ফাংশনগুলির একটি তালিকা দেয়। যদি আচরণটি পরিবর্তন করা হয় তবে তারা পরিবর্তে আই -i এর কল-টাইম মানকে আবদ্ধ করবে , সুতরাং আপনি সমস্ত ফাংশনগুলির তালিকা পাবেন 9

এটি অন্যথায় বাস্তবায়নের একমাত্র উপায় হ'ল আমি আবদ্ধ হওয়ার সাথে আরও বন্ধকরণ তৈরি করব, যেমন:

def make_func(i): return lambda: i
funcs = [make_func(i) for i in range(10)]

৩. অন্তর্মুখি

কোডটি বিবেচনা করুন:

def foo(a='test', b=100, c=[]):
   print a,b,c

আমরা inspectমডিউলটি ব্যবহার করে আর্গুমেন্ট এবং ডিফল্ট সম্পর্কে তথ্য পেতে পারি

>>> inspect.getargspec(foo)
(['a', 'b', 'c'], None, None, ('test', 100, []))

এই তথ্যটি ডকুমেন্ট জেনারেশন, রূপান্তরকাজ, সাজসজ্জা ইত্যাদির জন্য খুব দরকারী very

এখন ধরুন, খেলাপিদের আচরণটি এমনভাবে পরিবর্তিত হতে পারে যে এটি এর সমতুল্য:

_undefined = object()  # sentinel value

def foo(a=_undefined, b=_undefined, c=_undefined)
    if a is _undefined: a='test'
    if b is _undefined: b=100
    if c is _undefined: c=[]

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


1
আপনি প্রত্যেকের জন্য যদি একটি মানের পরিবর্তে ডিফল্ট আর্গুমেন্ট তৈরির জন্য কোনও ফাংশন উপস্থিত থাকে তবে আপনি অন্তর্মুখি অর্জন করতে পারেন। পরিদর্শন মডিউল কেবল যে ফাংশন কল করবে।
ইয়ারছু

@ সাইলেন্টগোস্ট: আমি কথা বলছি যদি আচরণটি এটি পুনরায় তৈরি করার জন্য পরিবর্তন করা হয়েছিল - এটি একবার তৈরি করা বর্তমান আচরণ এবং কেন পরিবর্তনীয় ডিফল্ট সমস্যা বিদ্যমান।
ব্রায়ান

1
@ আইয়ারচু: এটি ধরে নিয়েছে যে নির্মাণটি নিরাপদ (যেমন কোনও পার্শ্ব প্রতিক্রিয়া নেই)। আত্মদর্শী args করা উচিত নয় কি কিছু, কিন্তু নির্বিচারে কোড মূল্যায়নের ভাল প্রভাব থাকার শেষ পর্যন্ত পারে।
ব্রায়ান

1
একটি ভিন্ন ভাষার নকশা প্রায়শই কেবল আলাদাভাবে লেখার অর্থ। আপনার প্রথম উদাহরণটি সহজেই লিখিত হতে পারে: _ সস্তা = ব্যয়বহুল (); Def foo (আরগ = _ সস্তা), যদি আপনি বিশেষভাবে এটি পুনরায় মূল্যায়ন করতে না চান।
গ্লেন মেইনার্ড

@ গ্লেন - এটাই আমি "বাহ্যিকভাবে ভেরিয়েবলটি ক্যাশে" উল্লেখ করছিলাম - এটি কিছুটা ভারবস, এবং আপনি নিজের নামের ক্ষেত্রে অতিরিক্ত ভেরিয়েবলের সমাপ্তি ঘটান।
ব্রায়ান

55

পাইথনের প্রতিরক্ষা পাঁচ পয়েন্ট

  1. সরলতা : আচরণটি নিম্নোক্ত অর্থে সহজ: বেশিরভাগ লোক এই ফাঁদে পড়ে যায়, বেশ কয়েকবার নয়।

  2. ধারাবাহিকতা : পাইথন সর্বদা নাম নয়, বস্তুগুলি পাস করে। ডিফল্ট প্যারামিটারটি অবশ্যই ফাংশন শিরোনামের অংশ (ফাংশন বডি নয়)। সুতরাং এটি ফাংশন কল সময়ে নয়, মডিউল লোড সময় (এবং কেবলমাত্র মডিউল লোড সময়, নেস্ট করা ব্যতীত) মূল্যায়ন করা উচিত।

  3. দরকারীতা : ফ্রেডেরিক লুন্ড তার "পাইথনের ডিফল্ট প্যারামিটার মানগুলিতে" ব্যাখ্যা করার সাথে উল্লেখ করেছেন , উন্নত প্রোগ্রামিংয়ের জন্য বর্তমান আচরণটি বেশ কার্যকর হতে পারে। (সতর্কতার সাথে অল্পকরে ব্যবহার করা.)

  4. যথেষ্ট ডকুমেন্টেশন : অধিকাংশ মৌলিক পাইথন ডকুমেন্টেশন, টিউটোরিয়াল, ইস্যু জোরে একটি হিসাবে ঘোষণা করা হয় "গুরুত্বপূর্ণ সতর্কতা" মধ্যে প্রথম বিভাগের উপধারা "কার্যাবলী ডিফাইনিং আরও" । সতর্কতা এমনকি বোল্ডফেস ব্যবহার করে যা শিরোনামের বাইরে খুব কমই প্রয়োগ করা হয়। আরটিএফএম: সূক্ষ্ম ম্যানুয়ালটি পড়ুন।

  5. মেটা-লার্নিং : ফাঁদে পড়ে যাওয়া আসলে একটি খুব সহায়ক মুহূর্ত (কমপক্ষে আপনি যদি একজন প্রতিফলিত শিক্ষার্থী হন) তবে পরবর্তীকালে আপনি উপরের "ধারাবাহিকতা "টি আরও ভালভাবে বুঝতে পারবেন এবং এটি আপনাকে পাইথন সম্পর্কে একটি দুর্দান্ত বিষয় শিখিয়ে দেবে।


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

7
@ ওরিয়াদম, কোনও অপরাধ নয়, তবে আমি অবাক হয়েছি যে আপনি কীভাবে পাইথনটি আগে শিখেছেন তা না শিখলে। আমি এখন পাইথন শিখছি এবং এই সম্ভাব্য সমস্যাটি ডিফল্ট আর্গুমেন্টগুলির প্রথম উল্লেখের ঠিক পাশাপাশি সরকারী পাইথন টিউটোরিয়ালে উল্লেখ করা হয়েছে। (এই উত্তরের পয়েন্টে যেমন উল্লেখ করা হয়েছে।) আমি মনে করি যে নীতিটি আপনি উত্পাদন সফ্টওয়্যার তৈরি করতে যে ভাষা ব্যবহার করেন সেটির অফিসিয়াল ডক্সটি পড়ার জন্য — বরং সংবেদনশীলভাবে is
ওয়াইল্ডকার্ড

এছাড়াও, এটি অবাক করা হবে (আমার কাছে) যদি আমি যে ফাংশনটি করছি তার সাথে অজানা জটিলতার কোনও ফাংশন কল করা হয়েছিল।
ভ্যাটাইন

52

এই আচরণটি দ্বারা সহজে ব্যাখ্যা করা যায়:

  1. ফাংশন (শ্রেণি ইত্যাদি) ঘোষণাটি কেবল একবার কার্যকর করা হয়, সমস্ত ডিফল্ট মান অবজেক্ট তৈরি করে
  2. সবকিছু রেফারেন্স দ্বারা পাস করা হয়

তাই:

def x(a=0, b=[], c=[], d=0):
    a = a + 1
    b = b + [1]
    c.append(1)
    print a, b, c
  1. a পরিবর্তন হয় না - প্রতিটি অ্যাসাইনমেন্ট কল নতুন ইনট অবজেক্ট তৈরি করে - নতুন অবজেক্টটি প্রিন্ট করা হয়
  2. b পরিবর্তন হয় না - নতুন অ্যারেটি ডিফল্ট মান এবং মুদ্রিত থেকে তৈরি হয়
  3. c পরিবর্তন - ক্রিয়াকলাপ একই বস্তুতে সঞ্চালিত হয় - এবং এটি মুদ্রিত হয়

(আসলে, অ্যাড একটি খারাপ উদাহরণ, কিন্তু পূর্ণসংখ্যার অপরিবর্তনীয় এখনও হচ্ছে আমার মূল বিন্দু।)
Anon

খ [[], বি। এ সেট করে দেখুন __ ([1]) রিটার্ন [1] যোগ করেও বি বি ছেড়ে দেয় []] তালিকাগুলি পরিবর্তনযোগ্য হলেও এটি আমার চাগ্রিনে অনুধাবন করে। আমার খারাপ।
আনন

@ অ্যানন: আছে __iadd__, তবে এটি ইনট দিয়ে কাজ করে না। অবশ্যই. :-)
ভিকি

35

আপনি যা জিজ্ঞাসা করছেন এটি কেন:

def func(a=[], b = 2):
    pass

অভ্যন্তরীণভাবে এটির সাথে সমান নয়:

def func(a=None, b = None):
    a_default = lambda: []
    b_default = lambda: 2
    def actual_func(a=None, b=None):
        if a is None: a = a_default()
        if b is None: b = b_default()
    return actual_func
func = func()

সুস্পষ্টভাবে ফানক (কিছুই নয়, কিছুই নয়) কল করার ক্ষেত্রে বাদে আমরা এড়িয়ে যাব।

অন্য কথায়, ডিফল্ট প্যারামিটারগুলি মূল্যায়নের পরিবর্তে, কেন সেগুলি প্রতিটি সংরক্ষণ করে না, এবং ফাংশনটি বলা হয়ে গেলে সেগুলি মূল্যায়ন করবেন না?

একটি উত্তর সম্ভবত ঠিক সেখানে রয়েছে - এটি কার্যকরভাবে ডিফল্ট পরামিতিগুলির সাথে প্রতিটি ক্রিয়াকলাপ বন্ধ করে দেয়। এমনকি যদি এগুলি সমস্ত দোভাষীর মধ্যে লুকিয়ে থাকে এবং একটি সম্পূর্ণ বিকাশ বন্ধ না হয় তবে ডেটা কোথাও সংরক্ষণ করা উচিত। এটি ধীর হবে এবং আরও মেমরি ব্যবহার করবে।


6
এটি কোনও বন্ধ হওয়ার দরকার পড়বে না - এটির আরও ভাল উপায়টি মনে করে কেবল বাইটকোড তৈরি করে কোডের প্রথম লাইনটি ডিফল্ট করে দেওয়া হয় - সর্বোপরি আপনি যেভাবে যে কোনও মুহুর্তে দেহটি সংকলন করছেন - কোডের মধ্যে কোনও সত্যিকারের পার্থক্য নেই দেহের মধ্যে যুক্তি এবং কোডে।
ব্রায়ান

10
সত্য, তবে এটি পাইথনটিকে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে প্রবেশ করবে বর্গ। উল্লিখিত হিসাবে, সমস্যার চেয়ে সমস্যাটি আরও আশ্চর্যজনক হবে।
লেনার্ট রেজেব্রো

লেনার্টের সাথে একমত গিডো যেমন বলার অনুরাগী, প্রতিটি ভাষার বৈশিষ্ট্য বা স্ট্যান্ডার্ড লাইব্রেরির জন্য, সেখানে কেউ এটি ব্যবহার করছে।
জেসন বেকার 13

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

35

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

উদাহরণ:

def foo(a=[]):                 # the same problematic function
    a.append(5)
    return a

>>> somevar = [1, 2]           # an example without a default parameter
>>> foo(somevar)
[1, 2, 5]
>>> somevar
[1, 2, 5]                      # usually expected [1, 2]

সমাধান : একটি কপি
একটি একেবারে নিরাপদ সমাধান হয় copyবা deepcopyইনপুট বস্তুর প্রথম এবং তারপর কপি সঙ্গে যাই হোক না কেন না।

def foo(a=[]):
    a = a[:]     # a copy
    a.append(5)
    return a     # or everything safe by one line: "return a + [5]"

অনেকগুলি বিল্টিন পরিবর্তনযোগ্য প্রকারের মতো একটি অনুলিপি পদ্ধতি থাকে some_dict.copy()বা এর some_set.copy()মতো somelist[:]বা সহজে অনুলিপি করা যায় list(some_list)। প্রতিটি বস্তুর এছাড়াও দ্বারা অনুলিপি করা যায় copy.copy(any_object)বা আরো পুঙ্খানুপুঙ্খ copy.deepcopy()(আধুনিক দরকারী চপল বস্তুর চপল বস্তু থেকে গঠিত হয় থাকেন)। কিছু বস্তু মৌলিকভাবে "ফাইল" অবজেক্টের মতো পার্শ্ব প্রতিক্রিয়াগুলির উপর ভিত্তি করে থাকে এবং অনুলিপি করে অনুলিপি দ্বারা অনুলিপি করা যায় না। নকল

একই অনুরূপ প্রশ্নের জন্য উদাহরণস্বরূপ সমস্যা

class Test(object):            # the original problematic class
  def __init__(self, var1=[]):
    self._var1 = var1

somevar = [1, 2]               # an example without a default parameter
t1 = Test(somevar)
t2 = Test(somevar)
t1._var1.append([1])
print somevar                  # [1, 2, [1]] but usually expected [1, 2]
print t2._var1                 # [1, 2, [1]] but usually expected [1, 2]

এটি এই ফাংশন দ্বারা ফিরে আসা কোনও ঘটনার কোনও সর্বজনীন বৈশিষ্ট্যে সংরক্ষণ করা উচিত নয় । (ধরে নিই যে উদাহরণস্বরূপ ব্যক্তিগত বৈশিষ্ট্যগুলি এই শ্রেণীর বাইরে বা সাবক্ল্যাসের দ্বারা কনভেনশন দ্বারা সংশোধন করা উচিত নয় ie_var1 এটি একটি ব্যক্তিগত বৈশিষ্ট্য)

উপসংহার:
ইনপুট প্যারামিটার অবজেক্টগুলিকে জায়গায় (পরিবর্তন করা) পরিবর্তন করা উচিত নয় বা তাদের ফাংশন দ্বারা ফিরিয়ে দেওয়া কোনও বস্তুর সাথে আবদ্ধ করা উচিত নয়। (যদি আমরা পার্শ্ব প্রতিক্রিয়া ছাড়াই প্রোগ্রামিং পছন্দ করি যা দৃ which়ভাবে সুপারিশ করা হয়। উইকিটি "পার্শ্ব প্রতিক্রিয়া" সম্পর্কে দেখুন (প্রথম দুটি অনুচ্ছেদ এই প্রসঙ্গে সুস্পষ্ট।))

2)
কেবলমাত্র যদি আসল প্যারামিটারের পার্শ্ব প্রতিক্রিয়া প্রয়োজন হয় তবে ডিফল্ট প্যারামিটারে অযাচিত হয় তবে দরকারী সমাধানটিdef ...(var1=None): if var1 is None: var1 = [] আরও ..

3) কিছু ক্ষেত্রে ডিফল্ট প্যারামিটারগুলির পরিবর্তনীয় আচরণ কার্যকর


5
আমি আশা করি আপনি সচেতন থাকবেন পাইথন করছি না একটি কার্মিক প্রোগ্রামিং ভাষা।
ভিকি

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

1
আমি এই শেষ পর্যায়ে আরও যোগ করেছি যে পাইথনের অ্যাসাইনমেন্ট শব্দার্থবিজ্ঞানগুলি যেখানে প্রয়োজন সেখানে ডেটা অনুলিপি এড়ানোর জন্য স্পষ্টভাবে তৈরি করা হয়েছে, সুতরাং অনুলিপিগুলি (এবং বিশেষত গভীর কপিগুলির) তৈরি করা রান-টাইম এবং মেমরির ব্যবহার উভয়কেই বিরূপ প্রভাবিত করবে। সেহেতু এগুলি কেবলমাত্র যখন প্রয়োজন তখনই ব্যবহার করা উচিত, তবে নতুনদের প্রায়শই বুঝতে সমস্যা হয়।
হোল্ডেনওয়েব

1
@ হোল্ডেনওয়েব আমি সম্মত একটি অস্থায়ী অনুলিপি হ'ল সাধারন উপায় এবং কখনও কখনও একমাত্র সম্ভাব্য উপায় যে কীভাবে কোনও বহিরাগত ফাংশন থেকে মূল পরিবর্তনীয় ডেটা রক্ষা করতে পারে যা তাদের সম্ভাব্য রূপান্তর করে। ভাগ্যক্রমে এমন একটি ফাংশন যা অযৌক্তিকভাবে ডেটা সংশোধন করে তা একটি বাগ হিসাবে বিবেচিত হয় এবং তাই অস্বাভাবিক।
hynekcer

আমি এই উত্তরের সাথে একমত def f( a = None )আপনি যখন অন্য কিছু বোঝাতে চাইছেন তখন কেন নির্মাণের প্রস্তাব দেওয়া হচ্ছে তা আমি বুঝতে পারি না । অনুলিপি করা ঠিক আছে, কারণ আপনার পক্ষে যুক্তিগুলি পরিবর্তন করা উচিত নয়। এবং আপনি যখন করেন if a is None: a = [1, 2, 3], আপনি যেভাবেই তালিকাটি অনুলিপি করেন।
কোডো

30

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

>>> def foo(a):
    a.append(5)
    print a

>>> a  = [5]
>>> foo(a)
[5, 5]
>>> foo(a)
[5, 5, 5]
>>> foo(a)
[5, 5, 5, 5]
>>> foo(a)
[5, 5, 5, 5, 5]

এই কোডটিতে কোনও ডিফল্ট মান নেই, তবে আপনি ঠিক একই সমস্যা পান।

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

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

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


7
যদিও সম্পর্কিত, আমি মনে করি এটি স্বতন্ত্র আচরণ (যেমন আমরা "ইন-প্লেস" appendপরিবর্তনের প্রত্যাশা করি a)। প্রতিটি কলটিতে একটি ডিফল্ট পরিবর্তনযোগ্য পুনরায় ইনস্ট্যান্ট হয় না তা হ'ল "অপ্রত্যাশিত" বিট ... কমপক্ষে আমার জন্য। :)
অ্যান্ডি হেডেন

2
@ অ্যান্ডি হাইডেন যদি ফাংশনটি যুক্তিটি সংশোধন করে প্রত্যাশিত হয় তবে এটি কেন ডিফল্ট হবে?
মার্ক র্যানসম

@ মার্করান্সম কেবলমাত্র আমিই মনে করতে পারি cache={}। যাইহোক, আমি সন্দেহ করি যে এই "অন্তত বিস্মিততা" তখনই উপস্থিত হয় যখন আপনি যুক্তিটি পরিবর্তনের জন্য যে ফাংশনটি কল করছেন তার প্রত্যাশা (বা চান) করবেন না
অ্যান্ডি হেডেন

1
@ অ্যান্ডি হেডেন আমি সেই উত্তরটি এখানে রেখেছিলাম with আমার সম্পর্কে আপনি কী মনে করেন জানি। আমি আপনার উদাহরণটি সম্পূর্ণতার cache={}জন্য এটিতে যুক্ত করতে পারি ।
মার্ক

1
@ অ্যান্ডি হাইডেন আমার উত্তরের বক্তব্যটি হ'ল যদি আপনি দুর্ঘটনাক্রমে কোনও আর্গুমেন্টের ডিফল্ট মানটি পরিবর্তন করে অবাক হয়ে যান তবে আপনার আরও একটি বাগ রয়েছে, এটি হ'ল যখন আপনার কোডটি দুর্ঘটনাক্রমে একজন কলারের মান পরিবর্তন করতে পারে যখন ডিফল্টটি ব্যবহার করা হত না । এবং নোট করুন যে Noneআরগটি যদি None সমস্যাটি সমাধান না করে তবে আসল ডিফল্ট ব্যবহার ও নির্ধারণ করা (আমি এ কারণে এটি একটি বিরোধী নিদর্শন হিসাবে বিবেচনা করি)। আপনি যদি ত্রুটিযুক্ত ত্রুটি-বিচ্যুতি যুক্তি মানগুলি পরিবর্তন করে অন্য বাগটি ঠিক করে থাকেন তবে আপনি এই "বিস্ময়কর" আচরণটি কখনই লক্ষ্য করবেন না বা যত্ন করবেন না।
বেন 21

27

ইতিমধ্যে ব্যস্ত বিষয়, তবে আমি এখানে যা পড়েছি তা নীচে আমাকে অভ্যন্তরীণভাবে কীভাবে কাজ করছে তা বুঝতে সাহায্য করেছে:

def bar(a=[]):
     print id(a)
     a = a + [1]
     print id(a)
     return a

>>> bar()
4484370232
4484524224
[1]
>>> bar()
4484370232
4484524152
[1]
>>> bar()
4484370232 # Never change, this is 'class property' of the function
4484523720 # Always a new object 
[1]
>>> id(bar.func_defaults[0])
4484370232

2
আসলে a = a + [1]ওভারলোড হিসাবে আগতদের জন্য এটি কিছুটা বিভ্রান্তিকর হতে পারে a... এটিকে পরিবর্তন করে b = a + [1] ; print id(b)কোনও লাইন যুক্ত করার বিষয়টি বিবেচনা করুন a.append(2)। এটি আরও সুস্পষ্ট করে তুলবে যে +দুটি তালিকায় সর্বদা একটি নতুন তালিকা তৈরি করা হয় (বরাদ্দ করা হয়েছে b), পরিবর্তিত অবস্থায় aএখনও একই থাকতে পারে id(a)
জার্ন হিজ

25

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

def print_tuple(some_tuple=(1,2,3)):
    print some_tuple

print_tuple()        #1
print_tuple((1,2,3)) #2

আমি তোমাকে একটা ইঙ্গিত দিব। এখানে বিশ্রামহীনতা (দেখুন) http://docs.python.org/library/dis.html দেখুন ):

#1

0 LOAD_GLOBAL              0 (print_tuple)
3 CALL_FUNCTION            0
6 POP_TOP
7 LOAD_CONST               0 (None)
10 RETURN_VALUE

#2

 0 LOAD_GLOBAL              0 (print_tuple)
 3 LOAD_CONST               4 ((1, 2, 3))
 6 CALL_FUNCTION            1
 9 POP_TOP
10 LOAD_CONST               0 (None)
13 RETURN_VALUE

আমি সন্দেহ করি অভিজ্ঞ আচরণটির ব্যবহারিক ব্যবহার রয়েছে (কে বাগের প্রজনন ছাড়াই সত্যিকার অর্থে স্ট্যাটিক ভেরিয়েবল ব্যবহার করেছেন?)

যেহেতু আপনি দেখতে পারেন, সেখানে হয় একটি কার্যকারিতা সুবিধা যখন অপরিবর্তনীয় ডিফল্ট আর্গুমেন্ট ব্যবহার করে। এটি একটি পার্থক্য করতে পারে যদি এটি প্রায়শই বলা ফাংশন হয় বা ডিফল্ট আর্গুমেন্টটি তৈরি করতে দীর্ঘ সময় নেয় takes এছাড়াও, মনে রাখবেন যে পাইথন সি নয়, সি তে আপনার কাছে ধ্রুবকগুলি রয়েছে যা বেশ বিনামূল্যে। পাইথনে আপনার এই সুবিধা নেই।


24

পাইথন: মিউটেবল ডিফল্ট আর্গুমেন্ট

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

যখন তারা পরিবর্তনীয় হয়, যখন রূপান্তরিত হয় (উদাহরণস্বরূপ, এটিতে কোনও উপাদান যুক্ত করে) তারা পরপর কলগুলিতে পরিবর্তিত হয়।

তারা পরিবর্তিত হয় কারণ তারা প্রতিবার একই জিনিস।

সমান কোড:

যেহেতু ফাংশন অবজেক্টটি সংকলন ও তাত্ক্ষণিকভাবে তালিকা ফাংশনের সাথে আবদ্ধ, তাই:

def foo(mutable_default_argument=[]): # make a list the default argument
    """function that uses a list"""

এটি প্রায় ঠিক সমতুল্য:

_a_list = [] # create a list in the globals

def foo(mutable_default_argument=_a_list): # make it the default argument
    """function that uses a list"""

del _a_list # remove globals name binding

প্রদর্শন

এখানে একটি বিক্ষোভ রয়েছে - আপনি যাচাই করতে পারবেন যে তারা প্রতি বার তাদের দ্বারা রেফারেন্স করা হচ্ছে একই জিনিস

  • ফাংশনটি কোনও ফাংশনের সাথে সংকলন শেষ করার আগে তালিকাটি তৈরি করা হয়েছে তা দেখে
  • প্রতিটি সময় তালিকাটি রেফারেন্স করা হলে আইডি একই হয় তা পর্যবেক্ষণ করা হচ্ছে,
  • পর্যবেক্ষণ করে যে তালিকার পরিবর্তন হয় যখন এটি ব্যবহার করে এমন ফাংশনটিকে দ্বিতীয়বার বলা হয়,
  • উত্স থেকে আউটপুট মুদ্রিত হওয়া ক্রম পর্যবেক্ষণ করা হচ্ছে (যা আমি আপনার জন্য স্বাচ্ছন্দ্যের সাথে সংখ্যায়িত করেছি):

example.py

print('1. Global scope being evaluated')

def create_list():
    '''noisily create a list for usage as a kwarg'''
    l = []
    print('3. list being created and returned, id: ' + str(id(l)))
    return l

print('2. example_function about to be compiled to an object')

def example_function(default_kwarg1=create_list()):
    print('appending "a" in default default_kwarg1')
    default_kwarg1.append("a")
    print('list with id: ' + str(id(default_kwarg1)) + 
          ' - is now: ' + repr(default_kwarg1))

print('4. example_function compiled: ' + repr(example_function))


if __name__ == '__main__':
    print('5. calling example_function twice!:')
    example_function()
    example_function()

এবং এটি দিয়ে চালানো python example.py:

1. Global scope being evaluated
2. example_function about to be compiled to an object
3. list being created and returned, id: 140502758808032
4. example_function compiled: <function example_function at 0x7fc9590905f0>
5. calling example_function twice!:
appending "a" in default default_kwarg1
list with id: 140502758808032 - is now: ['a']
appending "a" in default default_kwarg1
list with id: 140502758808032 - is now: ['a', 'a']

এটি কি "সবচেয়ে স্বল্প বিস্মিত হওয়া" নীতি লঙ্ঘন করে?

মৃত্যুদন্ডের এই আদেশটি পাইথনের নতুন ব্যবহারকারীদের কাছে প্রায়শই বিভ্রান্ত হয়। যদি আপনি পাইথন এক্সিকিউশন মডেলটি বুঝতে পারেন তবে তা বেশ প্রত্যাশিত হয়ে যায়।

নতুন পাইথন ব্যবহারকারীদের জন্য সাধারণ নির্দেশনা:

তবে এ কারণেই নতুন ব্যবহারকারীদের স্বাভাবিক নির্দেশ হ'ল পরিবর্তে তাদের ডিফল্ট আর্গুমেন্ট তৈরি করা:

def example_function_2(default_kwarg=None):
    if default_kwarg is None:
        default_kwarg = []

এটি ডিফল্ট ব্যতীত অন্য কোনও যুক্তি অর্জন করেছি কিনা তা ফাংশনটি জানাতে সেন্ডিনেল অবজেক্ট হিসাবে নন সিঙ্গলটনকে ব্যবহার করে। যদি আমরা কোনও যুক্তি না পাই, তবে আমরা আসলে একটি নতুন খালি তালিকা ব্যবহার করতে চাই,[] ডিফল্ট হিসাবে ।

নিয়ন্ত্রণ প্রবাহের টিউটোরিয়াল বিভাগ যেমন বলে:

যদি আপনি না চান যে ডিফল্টটি পরবর্তী কলগুলির মধ্যে ভাগ করা যায় তবে আপনি তার পরিবর্তে এই ফাংশনটি লিখতে পারেন:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

24

সংক্ষিপ্ত উত্তরটি সম্ভবত "সংজ্ঞাটি কার্যকর করা হবে", সুতরাং পুরো যুক্তিটি কোনও কঠোর ধারণা দেয় না। আরও স্বীকৃত উদাহরণ হিসাবে আপনি এটি উদ্ধৃত করতে পারেন:

def a(): return []

def b(x=a()):
    print x

আশা করি এটি কার্যকর করার জন্য যথেষ্ট যে এটি কার্যকর করার সময় ডিফল্ট যুক্তি প্রকাশগুলি সম্পাদন করে না def বিবৃতি সহজ নয় বা বোধগম্য নয় বা উভয়ই নয়।

যদিও আপনি ডিফল্ট কনস্ট্রাক্টর ব্যবহার করার চেষ্টা করবেন তখন আমি সম্মত।


20

কিছুই না ব্যবহার করে একটি সাধারণ কাজ work

>>> def bar(b, data=None):
...     data = data or []
...     data.append(b)
...     return data
... 
>>> bar(3)
[3]
>>> bar(3)
[3]
>>> bar(3)
[3]
>>> bar(3, [34])
[34, 3]
>>> bar(3, [34])
[34, 3]

19

নিম্নলিখিতগুলি বিবেচনায় নিলে এই আচরণটি অবাক হওয়ার মতো নয়:

  1. অ্যাসাইনমেন্টের প্রচেষ্টার পরে কেবল-পাঠযোগ্য শ্রেণির বৈশিষ্ট্যগুলির আচরণ এবং that
  2. ফাংশনগুলি হ'ল বস্তু (গ্রহণযোগ্য উত্তরে ভালভাবে ব্যাখ্যা করা হয়েছে)।

(2) এর ভূমিকাটি এই থ্রেডে বিস্তৃত হয়েছে covered (1) সম্ভবত বিস্ময় সৃষ্টি করার কারণ, কারণ অন্য ভাষা থেকে আসার সময় এই আচরণটি "স্বজ্ঞাত" নয়।

(1) ক্লাস সম্পর্কিত পাইথন টিউটোরিয়ালে বর্ণিত হয়েছে । কেবলমাত্র পঠনযোগ্য শ্রেণির বৈশিষ্ট্যে একটি মান নির্ধারণের প্রয়াসে:

... অন্তঃস্থল স্কোপের বাইরে পাওয়া সমস্ত ভেরিয়েবলগুলি কেবল পঠনযোগ্য (এই ধরণের ভেরিয়েবলটি লেখার প্রচেষ্টা কেবলমাত্র অন্তর্নির্মিত স্কোপে একটি নতুন স্থানীয় ভেরিয়েবল তৈরি করবে, যার নামটি বহিরাগতভাবে পরিবর্তিত নয় এবং বহিরাগত পরিবর্তন হবে )।

মূল উদাহরণটি ফিরে দেখুন এবং উপরোক্ত বিষয়গুলি বিবেচনা করুন:

def foo(a=[]):
    a.append(5)
    return a

এখানে fooএকটি অবজেক্ট এবং aএটির foo(একটি উপলভ্য foo.func_defs[0]) বৈশিষ্ট্য । যেহেতু aএকটি তালিকা, aপরিবর্তনযোগ্য এবং এইভাবে এর একটি পঠন-লেখার বৈশিষ্ট্যfoo । ফাংশনটি তাত্ক্ষণিকভাবে স্বাক্ষর দ্বারা নির্ধারিত খালি তালিকায় এটি সূচনা করা হয়, এবং ফাংশন অবজেক্টের অস্তিত্ব থাকা অবধি পড়া এবং লেখার জন্য উপলব্ধ।

fooকোনও ডিফল্টকে ওভাররাইড না করে কল করা সেই ডিফল্টের মানটি ব্যবহার করে foo.func_defs। এই ক্ষেত্রে, ফাংশন অবজেক্টের কোড স্কোপের মধ্যে foo.func_defs[0]ব্যবহার aকরা হয়। পরিবর্তনগুলি aপরিবর্তন foo.func_defs[0], যা fooবস্তুর অংশ এবং কোডটি সম্পাদনের মধ্যে অবিচল থাকে foo

এখন, অন্যান্য ভাষার ডিফল্ট আর্গুমেন্ট আচরণ অনুকরণের ডকুমেন্টেশন থেকে উদাহরণের সাথে এটির তুলনা করুন , যেমন ফাংশন স্বাক্ষর ডিফল্টগুলি প্রতিবার ফাংশন সম্পাদন করার সময় ব্যবহৃত হয়:

def foo(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

টেকিং (1) এবং (2) একাউন্টে, এক দেখতে পারেন কেন এই আকাঙ্ক্ষিত আচরণ অর্জিত হয়-

  • যখন fooফাংশন অবজেক্টটি তাত্ক্ষণিকভাবে foo.func_defs[0]সেট করা হয় তখন সেট করা হয় None, একটি অপরিবর্তনীয় বস্তু।
  • ফাংশন অক্ষমতা সঙ্গে মৃত্যুদন্ড কার্যকর করা হলে (কোন প্যারামিটার নির্দিষ্ট সঙ্গে Lফাংশন কলে), foo.func_defs[0]( None) নামে স্থানীয় সুযোগ পাওয়া যায় L
  • উপরে L = [], অ্যাসাইনমেন্টটি সফল হতে পারে না foo.func_defs[0], কারণ সেই বৈশিষ্ট্যটি কেবল পঠনযোগ্য।
  • প্রতি (1) , এছাড়াও নামে একজন নতুন স্থানীয় পরিবর্তনশীল Lস্থানীয় সুযোগ তৈরি হয় এবং ফাংশন কল বাকি জন্য ব্যবহৃত। foo.func_defs[0]এইভাবে ভবিষ্যতের অনুরোধগুলির জন্য অপরিবর্তিত রয়েছে foo

19

আমি কোনও ফাংশনে ডিফল্ট তালিকার মানটি পাস করার জন্য একটি বিকল্প কাঠামো প্রদর্শন করতে যাচ্ছি (এটি অভিধানের সাথে সমানভাবে ভাল কাজ করে)।

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

ভুল পদ্ধতি (সম্ভবত ...) :

def foo(list_arg=[5]):
    return list_arg

a = foo()
a.append(6)
>>> a
[5, 6]

b = foo()
b.append(7)
# The value of 6 appended to variable 'a' is now part of the list held by 'b'.
>>> b
[5, 6, 7]  

# Although 'a' is expecting to receive 6 (the last element it appended to the list),
# it actually receives the last element appended to the shared list.
# It thus receives the value 7 previously appended by 'b'.
>>> a.pop()             
7

আপনি এটি ব্যবহার করে যাচাই করতে পারেন যে সেগুলি এক এবং একই বস্তু id:

>>> id(a)
5347866528

>>> id(b)
5347866528

প্রতি ব্রেট স্লাতকিনের "কার্যকর পাইথন: 59 আরও ভাল পাইথন লেখার নির্দিষ্ট উপায়", আইটেম 20: Noneগতিশীল ডিফল্ট আর্গুমেন্ট নির্দিষ্ট করার জন্য ব্যবহার এবং ডকাস্ট্রিংস (পৃষ্ঠা 48)

পাইথনে কাঙ্ক্ষিত ফলাফল অর্জনের জন্য কনভেনশনটি Noneহ'ল ডকাস্ট্রিংয়ের প্রকৃত আচরণের একটি ডিফল্ট মান প্রদান করা এবং ডকুমেন্ট করা।

এই বাস্তবায়নটি নিশ্চিত করে যে ফাংশনে প্রতিটি কল হয় ডিফল্ট তালিকাটি পায় বা অন্যথায় তালিকাটি ফাংশনে চলে গেছে।

পছন্দের পদ্ধতি :

def foo(list_arg=None):
   """
   :param list_arg:  A list of input values. 
                     If none provided, used a list with a default value of 5.
   """
   if not list_arg:
       list_arg = [5]
   return list_arg

a = foo()
a.append(6)
>>> a
[5, 6]

b = foo()
b.append(7)
>>> b
[5, 7]

c = foo([10])
c.append(11)
>>> c
[10, 11]

'ভুল পদ্ধতি' এর জন্য বৈধ ব্যবহারের মামলা থাকতে পারে যার মাধ্যমে প্রোগ্রামার ডিফল্ট তালিকার পরামিতি ভাগ করে নেওয়ার ইচ্ছা করেছিল, তবে এটি নিয়মের চেয়ে ব্যতিক্রম বেশি।


17

সমাধানগুলি এখানে:

  1. Noneআপনার ডিফল্ট মান (বা একটি নন object) হিসাবে ব্যবহার করুন , এবং রানটাইমে আপনার মানগুলি তৈরি করতে এটি স্যুইচ করুন; অথবা
  2. lambdaআপনার ডিফল্ট প্যারামিটার হিসাবে একটি ব্যবহার করুন এবং ডিফল্ট মান পেতে চেষ্টা ব্লকের মধ্যে এটি কল করুন (এটি ল্যাম্বডা বিমূর্তনের জন্য সাজানোর জিনিস is)

দ্বিতীয় বিকল্পটি দুর্দান্ত কারণ ফাংশনের ব্যবহারকারীরা কলযোগ্য হিসাবে যেতে পারে, যা ইতিমধ্যে বিদ্যমান থাকতে পারে (যেমন একটি type)


16

যখন আমরা এটি করি:

def foo(a=[]):
    ...

... কলার যদি একটিটির মান পাস না করে তবে আমরা aএকটি নামহীন তালিকায় যুক্তিটি বরাদ্দ করি ।

এই আলোচনার জন্য বিষয়গুলিকে আরও সহজ করার জন্য, আসুন অস্থায়ীভাবে নামহীন তালিকাটিকে একটি নাম দিন। কীভাবে pavlo?

def foo(a=pavlo):
   ...

যে কোনও সময়, যদি কলকারী আমাদের কী না বলে তা aআমরা পুনরায় ব্যবহার করি pavlo

যদি pavloপরিবর্তনীয় (পরিবর্তনযোগ্য) হয় এবং fooএটি সংশোধন করে শেষ হয়, পরের বার আমরা যে প্রভাবটি লক্ষ্য করি তা fooনির্দিষ্ট করে না বলা হয় a

সুতরাং এটিই আপনি দেখতে পান (মনে রাখবেন, এর pavloদ্বারা []] আরম্ভ করা হয়েছে:

 >>> foo()
 [5]

এখন, pavlo[5]।

foo()আবার কল করা আবার সংশোধন pavloকরে:

>>> foo()
[5, 5]

aকল করার সময় foo()সুনিশ্চিত pavloহওয়া নির্দিষ্ট করে ।

>>> ivan = [1, 2, 3, 4]
>>> foo(a=ivan)
[1, 2, 3, 4, 5]
>>> ivan
[1, 2, 3, 4, 5]

সুতরাং, pavloএখনও আছে [5, 5]

>>> foo()
[5, 5, 5]

16

আমি কখনও কখনও নীচের প্যাটার্নের বিকল্প হিসাবে এই আচরণটি কাজে লাগান:

singleton = None

def use_singleton():
    global singleton

    if singleton is None:
        singleton = _make_singleton()

    return singleton.use_me()

যদি singletonকেবলমাত্র এটি দ্বারা ব্যবহৃত হয় তবে use_singletonআমি প্রতিস্থাপন হিসাবে নীচের প্যাটার্নগুলি পছন্দ করি:

# _make_singleton() is called only once when the def is executed
def use_singleton(singleton=_make_singleton()):
    return singleton.use_me()

আমি এটি ক্লায়েন্ট ক্লাসগুলিকে ইনস্ট্যান্ট করার জন্য ব্যবহার করেছি যা বাহ্যিক সংস্থানগুলিতে অ্যাক্সেস করে এবং স্মৃতিচারণের জন্য ডিক্টস বা তালিকা তৈরি করার জন্যও।

যেহেতু আমি মনে করি না যে এই প্যাটার্নটি সুপরিচিত, তাই আমি ভবিষ্যতের ভুল বোঝাবুঝি থেকে রক্ষা করতে একটি সংক্ষিপ্ত মন্তব্য করব।


2
আমি স্মৃতিচারণের জন্য একটি সজ্জা যুক্ত করতে পছন্দ করি, এবং মেমোয়েজেশন ক্যাশে ফাংশন অবজেক্টের মধ্যে রাখি।
স্টেফানো বোরিনি

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

15

আপনি বস্তুর প্রতিস্থাপন করে এটি পেতে পারেন (এবং সেইজন্য সুযোগের সাথে টাই):

def foo(a=[]):
    a = list(a)
    a.append(5)
    return a

কুরুচিপূর্ণ, কিন্তু এটি কার্যকর।


3
আপনি ফাংশন দ্বারা প্রত্যাশিত আর্গুমেন্টগুলির ডকুমেন্ট করতে স্বয়ংক্রিয় ডকুমেন্টেশন জেনারেশন সফ্টওয়্যার ব্যবহার করছেন এমন ক্ষেত্রে এটি একটি দুর্দান্ত সমাধান। একটি = কোনওটি না রাখা এবং তারপরে [যদি কেউ নয় তবে একটি সেট করা পাঠককে কী প্রত্যাশিত তা এক নজরে বুঝতে সাহায্য করে না।
মাইকেল স্কট কুথবার্ট

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

এটি ঠিক এটি করার উপায়। পাইথন প্যারামিটারের একটি অনুলিপি তৈরি করে না, সুতরাং অনুলিপিটি স্পষ্টভাবে তৈরি করা আপনার পক্ষে। আপনার একবার অনুলিপি হয়ে গেলে কোনও অপ্রত্যাশিত পার্শ্ব প্রতিক্রিয়া ছাড়াই দয়া করে আপনার এটি পরিবর্তন করুন।
মার্ক র্যানসোম

13

এটি সত্য হতে পারে যে:

  1. কেউ প্রতিটি ভাষা / গ্রন্থাগার বৈশিষ্ট্য ব্যবহার করছে এবং
  2. আচরণটি এখানে পরিবর্তন করা অসুস্থ-পরামর্শ দেওয়া হবে, তবে

উপরের বৈশিষ্ট্যগুলি উভয়কে ধরে রাখা এবং এখনও অন্য একটি বিষয় বানাতে এটি সম্পূর্ণরূপে সামঞ্জস্যপূর্ণ:

  1. এটি একটি বিভ্রান্তিকর বৈশিষ্ট্য এবং এটি পাইথনের দুর্ভাগ্যজনক।

অন্যান্য উত্তরগুলি, বা তাদের মধ্যে কমপক্ষে কিছু হয় হয় পয়েন্ট 1 এবং 2 তৈরি করে তবে 3 নয়, বা পয়েন্ট 3 এবং ডাউনপ্লে পয়েন্ট 1 এবং 2 তৈরি করে তবে তিনটিই সত্য।

এটি সত্য হতে পারে যে এখানে ঘোড়াগুলির মধ্যে মাঝখানে স্যুইচিং করা উল্লেখযোগ্য ভাঙ্গনের জন্য জিজ্ঞাসা করবে এবং স্টেফানোর খোলার স্নিপেটটি স্বজ্ঞাতভাবে হ্যান্ডেল করার জন্য পাইথন পরিবর্তন করে আরও সমস্যা তৈরি হতে পারে। এবং এটি সত্য হতে পারে যে পাইথন ইন্টার্নালগুলি ভালভাবে জানত এমন কেউ তার পরিণতির একটি খনি ক্ষেত্রটি ব্যাখ্যা করতে পারে। যাহোক,

বিদ্যমান আচরণটি পাইথোনিক নয় এবং পাইথন সফল কারণ ভাষা সম্পর্কে খুব কমই কোথাও কোথাও কমপক্ষে বিস্মিত হওয়ার নীতি লঙ্ঘন করে কাছাকাছিএই খারাপভাবে এটি একেবারে উত্থাপন করা বুদ্ধিমানের হবে কি না এটি একটি আসল সমস্যা। এটি একটি ডিজাইনের ত্রুটি। আপনি যদি আচরণটি সন্ধান করার চেষ্টা করে ভাষাটি আরও ভালভাবে বুঝতে পারেন তবে আমি বলতে পারি যে সি ++ এই সমস্ত কিছু করে এবং আরও অনেক কিছু; আপনি নেভিগেট করে অনেক কিছু শিখেন, উদাহরণস্বরূপ, সূক্ষ্ম পয়েন্টার ত্রুটি। তবে এটি পাইথোনিক নয়: এই আচরণের মধ্যে অধ্যবসায় ধরে রাখার জন্য পাইথনের প্রতি যত্নশীল এমন লোকেরা ভাষার প্রতি আকৃষ্ট হন কারণ অন্যান্য ভাষার চেয়ে পাইথনের তুলনায় কম চমক রয়েছে। ছদ্মবেশী এবং কৌতূহলীরা পাইথনিস্টে পরিণত হয় যখন তারা কোনও কাজ পেতে খুব সামান্য সময় লাগে বলে আশ্চর্য হয়ে যায় - কোনও ডিজাইনের কারণে নয় - লুকানো লজিক ধাঁধা - যা পাইথনে আঁকানো প্রোগ্রামারদের অন্তর্দৃষ্টিগুলির বিরুদ্ধে কাটায় uts কারণ এটি জাস্ট ওয়ার্কস


6
-1 যদিও একটি ডিফেন্সেবল দৃষ্টিকোণ, এটি কোনও উত্তর নয় এবং আমি এর সাথে একমত নই। অনেকগুলি ব্যতিক্রম তাদের নিজস্ব কোণার ক্ষেত্রে রয়েছে get
মার্সিন

3
সুতরাং, এটি "আশ্চর্যরূপে অজ্ঞ" বলা যে পাইথনে এটি []] প্রতিবার ফাংশনটি বলা হওয়ার পরে [] থাকার একটি ডিফল্ট যুক্তি ধরে রাখার পক্ষে আরও অর্থবোধ করে?
ক্রিস্টোস হেওয়ার্ড

3
এবং দুর্ভাগ্যজনক আইডিয়ম হিসাবে কোনটিকেই ডিফল্ট আর্গুমেন্ট সেট করা হিসাবে বিবেচনা করা অজ্ঞ এবং তারপরে ফাংশনের সেটিংয়ের শরীরে যদি আর্গুমেন্ট == কোনওটি নয়: যুক্তি = []? এই মূর্খতাটিকে দুর্ভাগ্য হিসাবে বিবেচনা করা কি অজ্ঞ? কারণ প্রায়শই লোকেরা চায় যে একজন নির্দোষ আগত আগমনকারী আশা করবে, আপনি যদি চ (আর্গুমেন্ট = []) বরাদ্দ করেন তবে যুক্তিটি স্বয়ংক্রিয়ভাবে [] এর মানকে ডিফল্ট করে দেবে?
ক্রিস্টোস হেওয়ার্ড

3
তবে পাইথনে, ভাষার চেতনার অংশটি হ'ল আপনাকে খুব বেশি গভীর ডাইভ নিতে হবে না; অ্যারে.সোর্ট () কাজ করে এবং বাছাই, বিগ-ও, এবং ধ্রুবক সম্পর্কে আপনি যত কম বোঝেন তা নির্বিশেষে কাজ করে works অ্যারে বাছাইকরণ ব্যবস্থায় পাইথনের সৌন্দর্য, একটি উদাহরণ উদাহরণ দেওয়ার জন্য আপনাকে অভ্যন্তরীণ অঞ্চলে গভীর ডুব দেওয়ার দরকার নেই। এবং এটি অন্যভাবে বলতে গেলে পাইথনের সৌন্দর্য হ'ল জাস্ট ওয়ার্কস এমন কিছু পাওয়ার জন্য সাধারণভাবে বাস্তবায়নের জন্য গভীর ডুব নেওয়ার প্রয়োজন হয় না। এবং একটি কার্যতালিকা রয়েছে (... যদি যুক্তি == কোনওটিই নয়: যুক্তি = []), ব্যর্থ।
ক্রিস্টোস হেওয়ার্ড

3
একক হিসাবে, বিবৃতিটির x=[]অর্থ "একটি খালি তালিকার অবজেক্ট তৈরি করুন এবং এর সাথে 'x' নামটি আবদ্ধ করুন। সুতরাং, ইন def f(x=[]), একটি খালি তালিকা তৈরি করা হয়। এটি সর্বদা x এর সাথে আবদ্ধ হয় না, সুতরাং পরিবর্তে এটি ডিফল্ট সারোগেটের সাথে আবদ্ধ হয়। পরে যখন f () বলা হয় তখন ডিফল্টটিকে হ্যালো আউট করে এক্স দিয়ে আবদ্ধ করা হয়। যেহেতু এটি খালি তালিকাই ছিল যা দূরে শ্রেণিবদ্ধ ছিল, সেই একই তালিকাটি কেবলমাত্র এক্সকে বেঁধে রাখার জন্য উপলভ্য, এটির ভিতরে কোনও কিছু আটকে আছে কিনা whether কিভাবে এটি অন্যথায় হতে পারে?
জেরি বি

10

এটি কোনও ডিজাইনের ত্রুটি নয় । যে কেউ এই বিষয়ে ট্রিপ করে সে কিছু ভুল করছে।

এখানে 3 টি মামলা রয়েছে যা আপনি দেখতে পাচ্ছেন যেখানে আপনি এই সমস্যায় পড়তে পারেন:

  1. আপনি যুক্তিটি ফাংশনের একটি পার্শ্ব প্রতিক্রিয়া হিসাবে পরিবর্তন করতে চান। এই ক্ষেত্রে এটির কোনও ডিফল্ট যুক্তি থাকা কখনই বোধগম্য নয় । একমাত্র ব্যতিক্রম হ'ল আপনি যখন যুক্তি তালিকাকে ফাংশন বৈশিষ্ট্যগুলি রাখার জন্য অপব্যবহার করেন, উদাহরণস্বরূপ cache={}, এবং আপনার কোনও আসল যুক্তি দিয়ে ফাংশনটি কল করার আশা করা হবে না।
  2. আপনি যুক্তিটি মোছাবিহীন ছেড়ে দেওয়ার মনস্থ করেছেন, তবে আপনি দুর্ঘটনাক্রমে এটি সংশোধন করেছিলেন। এটি একটি বাগ, এটি ঠিক করুন।
  3. আপনি ফাংশনের অভ্যন্তরে ব্যবহারের জন্য যুক্তিটি সংশোধন করতে চান, তবে ফাংশনের বাইরে এই পরিবর্তনটি দৃশ্যমান হবে বলে আশা করেননি। সেক্ষেত্রে আপনার পক্ষে যুক্তিটির অনুলিপি তৈরি করা দরকার , এটি ডিফল্ট ছিল কি না! পাইথন কোনও কল-বাই-ভ্যালু ভাষা নয় তাই এটি আপনার জন্য অনুলিপি তৈরি করে না, আপনাকে এটি সম্পর্কে সুস্পষ্ট হওয়া দরকার।

প্রশ্নের উদাহরণটি 1 বা 3 বিভাগে পড়তে পারে It's এটি অদ্ভুত যে এটি উভয়ই উত্তীর্ণ তালিকার পরিবর্তন করে এবং এটিকে ফেরত দেয়; আপনার এক বা অন্যটি বাছাই করা উচিত।


"কিছু ভুল করা" এটি নির্ণয়। এটি বলেছিল, আমি মনে করি যে সময়গুলি ছিল = কোনটিই প্যাটার্ন কার্যকর নয় তবে সাধারণত আপনি সেই ক্ষেত্রে (2) পরিবর্তনযোগ্য পাস হলে পরিবর্তন করতে চান না। cache={}প্যাটার্ন সত্যিই এক সাক্ষাৎকারে-একমাত্র সমাধান, বাস্তব কোডে আপনি সম্ভবত চাই @lru_cache!
অ্যান্ডি হেডেন

9

এই "বাগ" আমাকে প্রচুর ওভারটাইমের কাজের সময় দিয়েছে! তবে আমি এটির সম্ভাব্য ব্যবহার দেখতে পাচ্ছি (তবে আমি মৃত্যুদন্ড কার্যকর করার সময় এটি এখনও পছন্দ করতাম)

আমি দরকারী উদাহরণ হিসাবে যা দেখছি তা আপনাকে দেব।

def example(errors=[]):
    # statements
    # Something went wrong
    mistake = True
    if mistake:
        tryToFixIt(errors)
        # Didn't work.. let's try again
        tryToFixItAnotherway(errors)
        # This time it worked
    return errors

def tryToFixIt(err):
    err.append('Attempt to fix it')

def tryToFixItAnotherway(err):
    err.append('Attempt to fix it by another way')

def main():
    for item in range(2):
        errors = example()
    print '\n'.join(errors)

main()

নিম্নলিখিত মুদ্রণ

Attempt to fix it
Attempt to fix it by another way
Attempt to fix it
Attempt to fix it by another way

8

শুধু এই ফাংশনটি পরিবর্তন করুন:

def notastonishinganymore(a = []): 
    '''The name is just a joke :)'''
    a = a[:]
    a.append(5)
    return a

7

আমি মনে করি যে এই প্রশ্নের উত্তর পাইথন কীভাবে পাইথন প্যারামিটারে ডেটা পাস করে (মান দ্বারা বা রেফারেন্স দিয়ে পাস করে), পরিবর্তনের নয় বা পাইথন কীভাবে "ডিএফ" বিবৃতি পরিচালনা করে।

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

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

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

আমার উত্তরটি আরও বৈধ করতে, আসুন দুটি অতিরিক্ত কোড দেখুন।

====== নং 2 ========

def foo(x, items=None):
    if items is None:
        items = []
    items.append(x)
    return items

foo(1)  #return [1]
foo(2)  #return [2]
foo(3)  #return [3]

[] একটি বস্তু, তাই হয় None তেমনও (পূর্ববর্তীটি পরিবর্তনযোগ্য, যদিও পূর্ববর্তীটি পরিবর্তনযোগ্য But তবে পরিবর্তনের প্রশ্নের সাথে কোনও সম্পর্ক নেই)। কেউই স্থানের কোথাও নেই তবে আমরা জানি এটি সেখানে আছে এবং সেখানে কেবল কোনওটিরই একটি অনুলিপি নেই। সুতরাং যতবারই foo আহ্বান করা হয়, আইটেমগুলি মূল্যায়ন করা হয় (কিছু উত্তরের বিপরীতে যে এটি একবারে মূল্যায়ন করা হয়) কোনওটি নয়, পরিষ্কার হওয়া, কোনটিরই রেফারেন্স (বা ঠিকানা)। তারপরে foo এ, আইটেমটি [] তে পরিবর্তিত হয়, অর্থাত্, অন্য কোনও বস্তুর দিকে নির্দেশ করে যার আলাদা ঠিকানা রয়েছে।

====== নং 3 =======

def foo(x, items=[]):
    items.append(x)
    return items

foo(1)    # returns [1]
foo(2,[]) # returns [2]
foo(3)    # returns [1,3]

Foo (1) এর অনুরোধ আইটেমগুলিকে একটি ঠিকানা সহ একটি তালিকা অবজেক্টের দিকে নির্দেশ করে [11], বলুন 11111111. তালিকার বিষয়বস্তু সিক্যুয়েলে foo ফাংশনটিতে 1- এ পরিবর্তিত হয়েছে , তবে ঠিকানাটি এখনও পরিবর্তন করা হয়নি 11111111 । তাহলে ফু (2, []) আসছে। যদিও foo (2, []) এ [] ফো-তে (1) কল করার সময় ডিফল্ট প্যারামিটারের মতো একই বিষয়বস্তু রয়েছে [1], তাদের ঠিকানাটি আলাদা! যেহেতু আমরা স্পষ্টভাবে প্যারামিটারটি সরবরাহ করি, তাই এই নতুনটির itemsঠিকানা নিতে হবে[] নতুনটির , 2222222 বলুন এবং কিছু পরিবর্তন করার পরে এটি ফিরিয়ে আনতে হবে। এখন foo (3) এক্সিকিউট করা হয়েছে। কেবলমাত্রxসরবরাহ করা হয়, আইটেমগুলি আবার তার ডিফল্ট মান নিতে হয়। ডিফল্ট মান কি? এটি foo ফাংশনটি সংজ্ঞায়িত করার সময় সেট করা হয়: 11111111 এ অবস্থিত তালিকার অবজেক্ট। সুতরাং আইটেমগুলি 11111111 এর একটি উপাদান সম্বোধন হিসাবে মূল্যায়ন করা হয়। 2222222 এ অবস্থিত তালিকায় একটি উপাদান 2 রয়েছে তবে এটি আইটেম দ্বারা কোনও নির্দেশিত নয় আরও অনেক কিছু। ফলস্বরূপ, 3 এর একটি সংযোজন items[1,3] করবে।

উপরের ব্যাখ্যা থেকে আমরা দেখতে পাব যে গৃহীত উত্তরে প্রস্তাবিত ইফবট ওয়েবপৃষ্ঠা এই প্রশ্নের প্রাসঙ্গিক উত্তর দিতে ব্যর্থ হয়েছে। এর চেয়ে বেশি কী, আমি মনে করি এফবোট ওয়েবপৃষ্ঠায় একটি পয়েন্ট ভুল। আমি ইউআই.বাটন সম্পর্কিত কোডটি সঠিক বলে মনে করি:

for i in range(10):
    def callback():
        print "clicked button", i
    UI.Button("button %s" % i, callback)

প্রতিটি বোতাম একটি পৃথক কলব্যাক ফাংশন ধারণ করতে পারে যা এর বিভিন্ন মান প্রদর্শন করবে i। আমি এটি দেখানোর জন্য একটি উদাহরণ সরবরাহ করতে পারি:

x=[]
for i in range(10):
    def callback():
        print(i)
    x.append(callback) 

আমরা যদি নির্বাহ x[7]()করি তবে আমরা প্রত্যাশা অনুযায়ী 7 পেয়ে যাব এবং x[9]()9 প্রদান করবে, এর আরও একটি মান i


5
আপনার শেষ কথাটি ভুল। এটি ব্যবহার করে দেখুন এবং আপনি দেখতে পাবেন যে x[7]()হয় 9
ডানকান

2
"পাইথন পাস দিয়ে প্রাথমিক ডেটা টাইপ করুন মান অনুসারে, স্থানীয় ভেরিয়েবলটিতে মানটির স্থানীয় কপি তৈরি করুন" সম্পূর্ণ ভুল। আমি অবাক হয়ে গিয়েছি যে কেউ স্পষ্টতই পাইথনকে খুব ভালভাবে জানতে পারে, তবুও মূলসূত্রগুলির মতো ভয়ঙ্কর ভুল ধারণা রয়েছে unders :-(
ভিকি

6

টিএলডিআর: সংজ্ঞায়িত সময়ের ডিফল্টগুলি সামঞ্জস্যপূর্ণ এবং কঠোরভাবে আরও অভিব্যক্তিপূর্ণ।


একটি ফাংশন সংজ্ঞায়িত করা দুটি স্কোপগুলিকে প্রভাবিত করে: ফাংশনটি ধারণ করে সংজ্ঞায়িত সুযোগ এবং ফাংশন দ্বারা অন্তর্ভুক্ত কার্যকরকরণের সুযোগ । যদিও এটি পুরোপুরি পরিষ্কার হয়ে গেছে যে কীভাবে মানচিত্রকে স্কোপগুলিতে ব্লক করে, প্রশ্নটি কোথায় def <name>(<args=defaults>):সম্পর্কিত:

...                           # defining scope
def name(parameter=default):  # ???
    ...                       # execution scope

def nameঅংশ নয় সংজ্ঞা সুযোগ মূল্যায়ন - আমরা চাই name, সেখানে উপলব্ধ হতে সব পরে। কেবল নিজের ভিতরেই ফাংশনটি মূল্যায়ন করা এটিকে অ্যাক্সেসযোগ্য করে তুলবে।

যেহেতু parameterএকটি ধ্রুবক নাম, আমরা একই সাথে এটি "মূল্যায়ন" করতে পারি def name। এই সুবিধা হিসাবে এটি একটি পরিচিত স্বাক্ষর সহ ফাংশন উৎপন্ন হয়েছে name(parameter=...):একটি খালি পরিবর্তে name(...):

এখন কবে মূল্যায়ন করবেন default?

ধারাবাহিকতা ইতিমধ্যে "সংজ্ঞাতে" বলেছে: সংজ্ঞায় অন্য সমস্ত কিছুই def <name>(<args=defaults>):সুনির্দিষ্টভাবে মূল্যায়ন করা হয়। এর অংশগুলি বিলম্ব করা অবাক করা পছন্দ হবে be

দুটি পছন্দই সমতুল্য নয়: যদি defaultসংজ্ঞা সময়ে মূল্যায়ন করা হয় তবে এটি কার্যকর করার সময়কেও প্রভাবিত করতে পারে। যদি defaultমৃত্যুদন্ড কার্যকর করার সময় মূল্যায়ন করা হয় তবে এটি সংজ্ঞা সময়কে প্রভাবিত করতে পারে না । "সংজ্ঞা অনুসারে" নির্বাচন করা উভয় ক্ষেত্রেই প্রকাশ করতে দেয়, যখন "মৃত্যুদণ্ড কার্যকর করার সময়" কেবলমাত্র একটিই প্রকাশ করতে পারে:

def name(parameter=defined):  # set default at definition time
    ...

def name(parameter=default):     # delay default until execution time
    parameter = default if parameter is None else parameter
    ...

"ধারাবাহিকতা ইতিমধ্যে" সংজ্ঞায় "বলেছে: সংজ্ঞায় অন্য সমস্ত কিছুই def <name>(<args=defaults>):সুনির্দিষ্টভাবে মূল্যায়ন করা হয়।" আমি মনে করি না সিদ্ধান্তটি উপসংহারটি অনুসরণ করে। কেবল দুটি জিনিস একই লাইনে থাকার অর্থ এই নয় যে তাদের একই ক্ষেত্রের মধ্যে মূল্যায়ন করা উচিত। defaultবাকী রেখার চেয়ে আলাদা জিনিস: এটি একটি অভিব্যক্তি। কোনও এক্সপ্রেশনকে মূল্যায়ন করা কোনও ফাংশন সংজ্ঞায়িত করা থেকে একেবারেই আলাদা প্রক্রিয়া।
LarsH

@LarsH ফাংশন সংজ্ঞা হয় হয় পাইথন মূল্যায়ন। এটি কোনও বিবৃতি ( def) বা অভিব্যক্তি ( lambda) থেকে হোক না কেন পরিবর্তন হয় না যে কোনও ফাংশন তৈরির অর্থ মূল্যায়ন - বিশেষত এর স্বাক্ষরের। এবং ডিফল্টগুলি কোনও ফাংশনের স্বাক্ষরের অংশ। মানে অক্ষমতা নেই আছে অবিলম্বে মূল্যায়ন করা - টাইপ ইঙ্গিত, নাও হতে পারে উদাহরণস্বরূপ। তবে এটি অবশ্যই প্রস্তাবিত করে যে তাদের উচিত উচিত না যদি না করার উপযুক্ত কারণ থাকে।
মিস্টারমিয়াগি

ঠিক আছে, একটি ফাংশন তৈরির অর্থ কিছুটা অর্থে মূল্যায়ন করা, তবে স্পষ্টতই সেই অর্থে নয় যে এর ভিতরে থাকা প্রতিটি অভিব্যক্তি সংজ্ঞা দেওয়ার সময় মূল্যায়ন করা হয়। বেশিরভাগ না। ফাংশন বডিটি "মূল্যায়ন" (উপযুক্ত উপস্থাপনায় পার্স করা) ছাড়া আর কোন সংজ্ঞা সময়ে স্বাক্ষরটি বিশেষত "মূল্যায়ন" করা হয় তা আমার কাছে স্পষ্ট নয়; যদিও ফাংশন বডিটিতে প্রকাশিত বিবরণগুলি পুরো অর্থে পরিষ্কারভাবে মূল্যায়ন করা হয় না। এই দৃষ্টিকোণ থেকে, ধারাবাহিকতা বলবে যে স্বাক্ষরটিতে প্রকাশগুলি "সম্পূর্ণরূপে" মূল্যায়ন করা উচিত নয়।
LarsH

আমার অর্থ এই নয় যে আপনি ভুল, কেবলমাত্র আপনার উপসংহারটি ধারাবাহিকতা থেকে অনুসরণ করে না।
LarsH

@ লার্শ ডিফল্টগুলি না কোনও দেহের অঙ্গ, না আমি দাবিও করছি না যে ধারাবাহিকতা কেবলমাত্র মানদণ্ড। উত্তরটি কীভাবে স্পষ্ট করা যায় আপনি একটি পরামর্শ দিতে পারেন?
মিস্টারমিয়াগি

3

প্রতিটি অন্যান্য উত্তর ব্যাখ্যা করে যে এটি আসলে কেন একটি দুর্দান্ত এবং কাঙ্ক্ষিত আচরণ, বা কেন আপনার প্রয়োজন হবে না। আমার সেই হঠকারীদের জন্য যারা ভাষাটি তাদের ইচ্ছার দিকে বাঁকানোর অধিকারটি ব্যবহার করতে চান, অন্যভাবে নয় not

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

import inspect
from copy import copy

def sanify(function):
    def wrapper(*a, **kw):
        # store the default values
        defaults = inspect.getargspec(function).defaults # for python2
        # construct a new argument list
        new_args = []
        for i, arg in enumerate(defaults):
            # allow passing positional arguments
            if i in range(len(a)):
                new_args.append(a[i])
            else:
                # copy the value
                new_args.append(copy(arg))
        return function(*new_args, **kw)
    return wrapper

এখন এই ডেকরেটারটি ব্যবহার করে আমাদের ফাংশনটিকে নতুনভাবে সংজ্ঞায়িত করা যাক:

@sanify
def foo(a=[]):
    a.append(5)
    return a

foo() # '[5]'
foo() # '[5]' -- as desired

এটি একাধিক যুক্তি গ্রহণকারী ফাংশনগুলির জন্য বিশেষভাবে ঝরঝরে। তুলনা করা:

# the 'correct' approach
def bar(a=None, b=None, c=None):
    if a is None:
        a = []
    if b is None:
        b = []
    if c is None:
        c = []
    # finally do the actual work

সঙ্গে

# the nasty decorator hack
@sanify
def bar(a=[], b=[], c=[]):
    # wow, works right out of the box!

এটি উল্লেখ করা গুরুত্বপূর্ণ যে উপরের সমাধানটি বিরতি পেয়েছে যদি আপনি কীওয়ার্ড আরগগুলি ব্যবহার করার চেষ্টা করেন তবে:

foo(a=[4])

সাজসজ্জারটি এর জন্য অনুমতি দেওয়ার জন্য সামঞ্জস্য করা যেতে পারে, তবে আমরা এটি পাঠকের অনুশীলন হিসাবে রেখেছি;)

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