শ্রেণীর সংজ্ঞায় একটি তালিকা বোধগম্য থেকে শ্রেণি ভেরিয়েবল অ্যাক্সেস করা


174

শ্রেণীর সংজ্ঞায়নের মধ্যে তালিকা বোধ থেকে আপনি অন্যান্য শ্রেণীর ভেরিয়েবলগুলি কীভাবে অ্যাক্সেস করবেন? পাইথন 2 এ নিম্নলিখিতটি কাজ করে তবে পাইথন 3 এ ব্যর্থ হয়:

class Foo:
    x = 5
    y = [x for i in range(1)]

পাইথন ৩.২ ত্রুটিটি দেয়:

NameError: global name 'x' is not defined

চেষ্টা করাও Foo.xকাজ করে না। পাইথন 3 এ এটি কীভাবে করবেন সে সম্পর্কে কোনও ধারণা?

কিছুটা জটিল জটিল অনুপ্রেরণার উদাহরণ:

from collections import namedtuple
class StateDatabase:
    State = namedtuple('State', ['name', 'capital'])
    db = [State(*args) for args in [
        ['Alabama', 'Montgomery'],
        ['Alaska', 'Juneau'],
        # ...
    ]]

এই উদাহরণে, apply()একটি শালীন কাজ হতে পারে, কিন্তু এটি দুঃখের সাথে পাইথন 3 থেকে সরানো হয়েছে।


আপনার ত্রুটির বার্তাটি ভুল। আমি NameError: global name 'x' is not definedপাইথন 3.2 এবং 3.3 এ যা যা আমি প্রত্যাশা করতাম।
মার্টিজন পিটারস

আকর্ষণীয় ... শ্রেণীর সংজ্ঞাটি বাইরে বেরোনোর ​​পরে একটি স্পষ্টতই কাজটি হ'ল y নির্ধারণ করা। Foo.y = [আমি রেঞ্জ ইন (1) এর জন্য Foo.x]
জিপিএস

3
ডুপ্লিকেটের সাথে মার্টিজন-পাইটারের লিঙ্কটি সঠিক, এখানে + ম্যাট-বি থেকে একটি মন্তব্য রয়েছে যার সাথে ব্যাখ্যা রয়েছে: পাইথন ২.7 তালিকা অনুধাবনের নিজস্ব নাম স্থান নেই (সেট বা ডিক বোঝার বা জেনারেটরের এক্সপ্রেশনগুলির বিপরীতে ... আপনার প্রতিস্থাপন করুন [ ] এটি কার্যকর অবস্থায় দেখতে {} সহ)। এঁদের সবার নিজস্ব নিজস্ব স্থান 3 3.
জিপিএস

@ জিপিএস: বা শ্রেণি সংজ্ঞা স্যুটটিতে একটি (অস্থায়ী) ফাংশন byুকিয়ে একটি নেস্টেড স্কোপ ব্যবহার করুন।
মার্টিজন পিটারস

আমি সবেমাত্র 2.7.11 এ পরীক্ষা করেছি। নামের ত্রুটি পেয়েছে
জুনচাও গু

উত্তর:


244

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

কেন; বা, এটিতে সরকারী শব্দ

পাইথন 3-তে, তালিকা অনুধাবনগুলিকে তাদের স্থানীয় ভেরিয়েবলগুলি আশেপাশের স্কোপ থেকে রক্তপাত রোধ করতে তাদের নিজস্ব একটি যথাযথ সুযোগ (স্থানীয় নেমস্পেস) দেওয়া হয়েছিল (দেখুন পাইথনের তালিকার বোঝাপড়ার পরেও পাইথনের তালিকা বোঝার নামগুলি পুনরায় প্রত্যাবর্তন করুন this এটি ঠিক কি? )? মডিউল বা কোনও ফাংশনে যেমন তালিকা বোধগম্যতা ব্যবহার করার সময় তা দুর্দান্ত তবে ক্লাসে স্কোপিংটি সামান্য, আহ, বিস্ময়কর

এটি পিপ 227 তে নথিভুক্ত করা হয়েছে :

শ্রেণীর ক্ষেত্রের নামগুলি অ্যাক্সেসযোগ্য নয়। নামগুলি অভ্যন্তরীণ ঘেরের ফাংশন স্কোপে সমাধান করা হয়। যদি কোনও শ্রেণি সংজ্ঞা নেস্টেড স্কোপগুলির একটি শৃঙ্খলে ঘটে তবে রেজোলিউশন প্রক্রিয়া শ্রেণীর সংজ্ঞাগুলি এড়িয়ে যায়।

এবং classযৌগিক বিবৃতি নথি :

ক্লাসের স্যুটটি নতুন এক্সিকিউশন ফ্রেমে (বিভাগের নামকরণ এবং বাঁধাই করা বিভাগ দেখুন ) সদ্য নির্মিত স্থানীয় নেমস্পেস এবং আসল গ্লোবাল নেমস্পেস ব্যবহার করে কার্যকর করা হয়। (সাধারণত, স্যুটটিতে কেবলমাত্র ফাংশন সংজ্ঞা থাকে)) যখন শ্রেণীর স্যুটটি কার্যকর করা শেষ করে, তখন তার কার্যকরকরণের ফ্রেমটি বাতিল করা হয় তবে এর স্থানীয় নেমস্পেস সংরক্ষণ করা হয়[4] বেস শ্রেণীর জন্য উত্তরাধিকার তালিকা এবং বৈশিষ্ট্য অভিধানের জন্য সংরক্ষিত স্থানীয় নেমস্পেস ব্যবহার করে একটি শ্রেণি অবজেক্ট তৈরি করা হয়।

জোর আমার; এক্সিকিউশন ফ্রেম হ'ল অস্থায়ী সুযোগ।

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

শেষ, তবে অবশ্যই নিখুঁতভাবে নয়, এক্সিকিউশন মডেল ডকুমেন্টেশনের লিঙ্কযুক্ত নামকরণ এবং বাধ্যতামূলক বিভাগে ক্লাস স্কোপগুলি স্পষ্টভাবে উল্লেখ করা হয়েছে:

ক্লাস ব্লকে সংজ্ঞায়িত নামের ক্ষেত্রগুলি ক্লাস ব্লকের মধ্যে সীমাবদ্ধ; এটি পদ্ধতির কোড ব্লকগুলিতে প্রসারিত হয় না - এর মধ্যে বোধগম্যতা এবং জেনারেটর এক্সপ্রেশন রয়েছে যেহেতু তারা কোনও ফাংশন স্কোপ ব্যবহার করে প্রয়োগ করা হয়েছে implemented এর অর্থ হল যে নিম্নলিখিতগুলি ব্যর্থ হবে:

class A:
     a = 42
     b = list(a + i for i in range(10))

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

# Same error, in Python 2 or 3
y = {x: x for i in range(1)}

(ছোট) ব্যতিক্রম; বা, কেন একটি অংশ এখনও কাজ করতে পারে

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

y = [x for i in range(1)]
#               ^^^^^^^^

সুতরাং, xএই অভিব্যক্তিটি ব্যবহার করে কোনও ত্রুটি ঘটবে না:

# Runs fine
y = [i for i in range(x)]

এটি কেবল বহিরাগততম পুনরাবৃত্তীয় ক্ষেত্রে প্রযোজ্য; যদি কোনও forবোধগমের একাধিক ধারা থাকে তবে অভ্যন্তরীণ forধারাগুলির পুনরাবৃত্তিগুলি বোঝার ক্ষেত্রগুলিতে মূল্যায়ন করা হয়:

# NameError
y = [i for i in range(1) for j in range(x)]

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

ফণা নীচে খুঁজছেন; বা, আপনি যা চেয়েছিলেন তার চেয়ে আরও বিশদ উপায়

আপনি disমডিউলটি ব্যবহার করে এটি সমস্ত ক্রিয়াকলাপে দেখতে পাবেন । আমি নীচের উদাহরণগুলিতে পাইথন ৩.৩ ব্যবহার করছি, কারণ এতে যোগ্য নাম যুক্ত করা হয়েছে যা আমরা পরিদর্শন করতে চাইলে কোড বস্তুগুলি সুন্দরভাবে চিহ্নিত করে। উত্পাদিত বাইটকোড অন্যথায় কার্যকরীভাবে পাইথন ৩.২ এর মতো ical

ক্লাস তৈরি করতে পাইথন মূলত পুরো স্যুটটি নিয়ে যায় যা শ্রেণিবদ্ধ শরীর তৈরি করে (সুতরাং সবকিছুই এক class <name>:লাইনের চেয়ে আরও বেশি এক স্তরকে আবদ্ধ করে ) এবং এটি কার্যকর করে যে এটি কোনও ফাংশন:

>>> import dis
>>> def foo():
...     class Foo:
...         x = 5
...         y = [x for i in range(1)]
...     return Foo
... 
>>> dis.dis(foo)
  2           0 LOAD_BUILD_CLASS     
              1 LOAD_CONST               1 (<code object Foo at 0x10a436030, file "<stdin>", line 2>) 
              4 LOAD_CONST               2 ('Foo') 
              7 MAKE_FUNCTION            0 
             10 LOAD_CONST               2 ('Foo') 
             13 CALL_FUNCTION            2 (2 positional, 0 keyword pair) 
             16 STORE_FAST               0 (Foo) 

  5          19 LOAD_FAST                0 (Foo) 
             22 RETURN_VALUE         

প্রথমে শ্রেণীর বডির LOAD_CONSTজন্য কোনও কোড অবজেক্ট লোড করা হয় Foo, তারপরে এটিকে একটি ফাংশনে পরিণত করে এবং কল করে। এই কলটির ফলাফলটি তখন শ্রেণীর নাম স্থান তৈরি করতে ব্যবহৃত হয়, এটির __dict__। এ পর্যন্ত সব ঠিকই.

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

এখানে মনে রাখা গুরুত্বপূর্ণ বিষয় হ'ল পাইথন সংকলনের সময় এই কাঠামোগুলি তৈরি করে; classস্যুট একটি কোড বস্তু (হয় <code object Foo at 0x10a436030, file "<stdin>", line 2>) যে ইতিমধ্যে কম্পাইল করা হয়।

আসুন সেই কোড অবজেক্টটি পরিদর্শন করি যা ক্লাস বডি নিজেই তৈরি করে; কোড অবজেক্টগুলির একটি co_constsকাঠামো রয়েছে:

>>> foo.__code__.co_consts
(None, <code object Foo at 0x10a436030, file "<stdin>", line 2>, 'Foo')
>>> dis.dis(foo.__code__.co_consts[1])
  2           0 LOAD_FAST                0 (__locals__) 
              3 STORE_LOCALS         
              4 LOAD_NAME                0 (__name__) 
              7 STORE_NAME               1 (__module__) 
             10 LOAD_CONST               0 ('foo.<locals>.Foo') 
             13 STORE_NAME               2 (__qualname__) 

  3          16 LOAD_CONST               1 (5) 
             19 STORE_NAME               3 (x) 

  4          22 LOAD_CONST               2 (<code object <listcomp> at 0x10a385420, file "<stdin>", line 4>) 
             25 LOAD_CONST               3 ('foo.<locals>.Foo.<listcomp>') 
             28 MAKE_FUNCTION            0 
             31 LOAD_NAME                4 (range) 
             34 LOAD_CONST               4 (1) 
             37 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
             40 GET_ITER             
             41 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
             44 STORE_NAME               5 (y) 
             47 LOAD_CONST               5 (None) 
             50 RETURN_VALUE         

উপরের বাইটোকোড শ্রেণিবৃদ্ধি তৈরি করে। ফাংশনটি কার্যকর করা হয় এবং ফলস্বরূপ locals()নামের স্থানটি ধারণ করে xএবং yশ্রেণি তৈরি করতে ব্যবহৃত হয় (এটি xবিশ্বব্যাপী হিসাবে সংজ্ঞায়িত না হওয়ায় এটি কাজ করে না )। লক্ষ্য করুন সংরক্ষণকারী পর 5x, এটা অন্য কোড বস্তুর লোড; যে তালিকা অনুধাবন; এটি ক্লাস বডি যেমন ঠিক একটি ফাংশন বস্তুতে আবৃত হয়; তৈরি ফাংশনটি একটি অবস্থানগত আর্গুমেন্ট গ্রহণ করে, range(1)এর লুপিং কোডের জন্য ব্যবহারযোগ্য পুনরাবৃত্তকারীকে একটি পুনরুক্তকারীর কাছে কাস্ট করে। বাইটকোডে যেমন দেখানো হয়েছে, range(1)শ্রেণি স্কোপে মূল্যায়ন করা হয়।

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

পাইথন ২.x এর পরিবর্তে সেখানে ইনলাইন বাইকোড ব্যবহার করে, এখানে পাইথন ২.7 থেকে আউটপুট পাওয়া গেছে:

  2           0 LOAD_NAME                0 (__name__)
              3 STORE_NAME               1 (__module__)

  3           6 LOAD_CONST               0 (5)
              9 STORE_NAME               2 (x)

  4          12 BUILD_LIST               0
             15 LOAD_NAME                3 (range)
             18 LOAD_CONST               1 (1)
             21 CALL_FUNCTION            1
             24 GET_ITER            
        >>   25 FOR_ITER                12 (to 40)
             28 STORE_NAME               4 (i)
             31 LOAD_NAME                2 (x)
             34 LIST_APPEND              2
             37 JUMP_ABSOLUTE           25
        >>   40 STORE_NAME               5 (y)
             43 LOAD_LOCALS         
             44 RETURN_VALUE        

কোনও কোড অবজেক্ট লোড হয় না, পরিবর্তে একটি FOR_ITERলুপ ইনলাইন চালানো হয়। পাইথন ৩.x এ, তালিকা জেনারেটরকে তার নিজস্ব একটি যথাযথ কোড অবজেক্ট দেওয়া হয়েছিল যার অর্থ এটির নিজস্ব সুযোগ রয়েছে।

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

>>> foo.__code__.co_consts[1].co_consts
('foo.<locals>.Foo', 5, <code object <listcomp> at 0x10a385420, file "<stdin>", line 4>, 'foo.<locals>.Foo.<listcomp>', 1, None)
>>> dis.dis(foo.__code__.co_consts[1].co_consts[2])
  4           0 BUILD_LIST               0 
              3 LOAD_FAST                0 (.0) 
        >>    6 FOR_ITER                12 (to 21) 
              9 STORE_FAST               1 (i) 
             12 LOAD_GLOBAL              0 (x) 
             15 LIST_APPEND              2 
             18 JUMP_ABSOLUTE            6 
        >>   21 RETURN_VALUE         

বাইটকোডের এই অংশটি প্রথম আর্গুমেন্টকে ( range(1)পুনরুক্তিকারী) লোড করে এবং পাইথন ২.x সংস্করণের মতো এটি FOR_ITERলুপ করে এবং এর আউটপুট তৈরি করে।

আমরা সংজ্ঞায়িত ছিল xমধ্যে fooফাংশন পরিবর্তে, xএকটি সেল পরিবর্তনশীল (কোষ নেস্টেড সুযোগ পড়ুন) হতে হবে:

>>> def foo():
...     x = 2
...     class Foo:
...         x = 5
...         y = [x for i in range(1)]
...     return Foo
... 
>>> dis.dis(foo.__code__.co_consts[2].co_consts[2])
  5           0 BUILD_LIST               0 
              3 LOAD_FAST                0 (.0) 
        >>    6 FOR_ITER                12 (to 21) 
              9 STORE_FAST               1 (i) 
             12 LOAD_DEREF               0 (x) 
             15 LIST_APPEND              2 
             18 JUMP_ABSOLUTE            6 
        >>   21 RETURN_VALUE         

LOAD_DEREFপরোক্ষভাবে লোড করা হবে xকোড বস্তুর সেল বস্তু থেকে:

>>> foo.__code__.co_cellvars               # foo function `x`
('x',)
>>> foo.__code__.co_consts[2].co_cellvars  # Foo class, no cell variables
()
>>> foo.__code__.co_consts[2].co_consts[2].co_freevars  # Refers to `x` in foo
('x',)
>>> foo().y
[2]

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

>>> def spam(x):
...     def eggs():
...         return x
...     return eggs
... 
>>> spam(1).__code__.co_freevars
('x',)
>>> spam(1)()
1
>>> spam(1).__closure__
>>> spam(1).__closure__[0].cell_contents
1
>>> spam(5).__closure__[0].cell_contents
5

সুতরাং, সংক্ষেপে বলা:

  • তালিকার বোধগম্যতা পাইথন 3 এ তাদের নিজস্ব কোড অবজেক্টগুলি পান এবং ফাংশন, জেনারেটর বা বোঝার জন্য কোড অবজেক্টগুলির মধ্যে কোনও পার্থক্য নেই; বোধগম্য কোড অবজেক্টগুলি একটি অস্থায়ী ফাংশন অবজেক্টে আবৃত থাকে এবং সাথে সাথে তাকে ডাকা হয়।
  • কোড অবজেক্টগুলি সংকলন সময়ে তৈরি করা হয় এবং কোনও অ-স্থানীয় ভেরিয়েবল কোডের নেস্টেড স্কোপের ভিত্তিতে গ্লোবাল বা ফ্রি ভেরিয়েবল হিসাবে চিহ্নিত করা হয়। শ্রেণীর সংস্থাকে সেই পরিবর্তনশীলগুলি অনুসন্ধান করার সুযোগ হিসাবে বিবেচনা করা হয় না
  • কোডটি কার্যকর করার সময় পাইথনের কেবলমাত্র গ্লোবালগুলি অনুসন্ধান করা উচিত, বা বর্তমানে সম্পাদনকারী অবজেক্টটির সমাপ্তি। যেহেতু সংকলক ক্লাস বডিটিকে স্কোপ হিসাবে অন্তর্ভুক্ত করেনি তাই অস্থায়ী ফাংশন নেমস্পেস বিবেচনা করা হয় না।

একটি workaround; বা, এটি সম্পর্কে কি করতে হবে

আপনি যদি xকোনও ফাংশনের মতো ভেরিয়েবলের জন্য একটি সুস্পষ্ট সুযোগ তৈরি করতে থাকেন তবে তালিকা বোধের জন্য আপনি শ্রেণি-স্কোপ ভেরিয়েবলগুলি ব্যবহার করতে পারেন:

>>> class Foo:
...     x = 5
...     def y(x):
...         return [x for i in range(1)]
...     y = y(x)
... 
>>> Foo.y
[5]

'অস্থায়ী' yফাংশনটি সরাসরি বলা যেতে পারে; আমরা যখন এর রিটার্ন মান দিয়ে থাকি তখন আমরা এটিকে প্রতিস্থাপন করি। তার সুযোগ হয় যখন সমাধানে বিবেচিত x:

>>> foo.__code__.co_consts[1].co_consts[2]
<code object y at 0x10a5df5d0, file "<stdin>", line 4>
>>> foo.__code__.co_consts[1].co_consts[2].co_cellvars
('x',)

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

সর্বোত্তম কাজটি হ'ল পরিবর্তে কেবলমাত্র __init__একটি উদাহরণ পরিবর্তনশীল তৈরি করতে ব্যবহার করা:

def __init__(self):
    self.y = [self.x for i in range(1)]

এবং নিজেকে ব্যাখ্যা করার জন্য সমস্ত মাথা-ঘাটানো, এবং প্রশ্নগুলি এড়িয়ে চলুন। আপনার নিজের কংক্রিটের উদাহরণের জন্য, আমি এমনকি namedtupleক্লাসে স্টোরটি সংরক্ষণ করব না ; হয় আউটপুট সরাসরি ব্যবহার করুন (উত্পন্ন শ্রেণিটি মোটেও সংরক্ষণ করবেন না), অথবা একটি বিশ্বব্যাপী ব্যবহার করুন:

from collections import namedtuple
State = namedtuple('State', ['name', 'capital'])

class StateDatabase:
    db = [State(*args) for args in [
       ('Alabama', 'Montgomery'),
       ('Alaska', 'Juneau'),
       # ...
    ]]

21
বাঁধাই ঠিক করতে আপনি একটি y = (lambda x=x: [x for i in range(1)])()
ল্যাম্বদাও

3
@ ক্যাটমুর: হুবহু, lambdaকেবলমাত্র অনামিকার কাজগুলি।
মার্টিজন পিটারস

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

9
কেন কিছু স্বজ্ঞাতভাবে কাজ করে না তা ব্যাখ্যা করার জন্য যদি প্রযুক্তিগত তথ্যের একটি পৃষ্ঠার প্রয়োজন হয়, আমি এটিকে একটি বাগ বলি।
জোনাথন

5
@ জোনাথনলিডার্স: এটিকে বাগ বলবেন না, একে ট্রেড অফ বলুন । আপনি যদি ক এবং খ চান, তবে তাদের মধ্যে একটি মাত্র পেতে পারেন তবে আপনি কীভাবে সিদ্ধান্ত নেবেন না কেন, কিছু পরিস্থিতিতে আপনি ফলাফলটি অপছন্দ করবেন। এটাই জীবন.
লুটজ প্রিচেল্ট

15

আমার মতে এটি পাইথন 3 এ ত্রুটিযুক্ত I আমি আশা করি তারা এটি পরিবর্তন করে।

ওল্ড ওয়ে (২.7 এ কাজ করে, NameError: name 'x' is not defined3+ এ ছোঁড়ে ):

class A:
    x = 4
    y = [x+i for i in range(1)]

দ্রষ্টব্য: কেবল এটিকে স্কুপ করা এটিকে A.xসমাধান করবে না

নতুন উপায় (3+ এ কাজ করে):

class A:
    x = 4
    y = (lambda x=x: [x+i for i in range(1)])()

সিনট্যাক্সটি যেহেতু কুরুচিপূর্ণ তাই আমি সাধারণত আমার কনস্ট্রাক্টরের সমস্ত ক্লাস ভেরিয়েবলগুলি আরম্ভ করি


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

4
এবং আমি নোট করেছি যে আপনার উত্তরসূচী আমার উত্তরটি ইতিমধ্যে যা বলেছে ঠিক তেমনটাই করে: একটি নতুন সুযোগ তৈরি করুন (একটি ল্যাম্বডা এখানে কোনও defফাংশন তৈরির জন্য ব্যবহারের চেয়ে আলাদা নয় )।
মার্টিজন পিটারস

1
হাঁ। এক নজরে কাজের আশেপাশের সাথে উত্তর পাওয়া খুব ভাল, যদিও এটি ভাষাটি যেভাবে কাজ করে তার পার্শ্ব-প্রতিক্রিয়া হলে (এবং তাই এটি পরিবর্তিত হবে না) এইটি ভুলভাবে আচরণটিকে বাগ হিসাবে বর্ণনা করে (
jsbueno

এটি একটি ভিন্ন সমস্যা, এটি পাইথন 3-এ আসলে কোনও সমস্যা নয় It এটি কেবলমাত্র আইপিথনে ঘটে যখন আপনি এটিকে এম্বেড মোডে বলুন বলুন python -c "import IPython;IPython.embed()"। সরাসরি বলুন আইপিথন চালান ipythonএবং সমস্যাটি অদৃশ্য হয়ে যাবে।
রিয়াজ রিজভি

6

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

class Foo:

    # A class-level variable.
    X = 10

    # I can use that variable to define another class-level variable.
    Y = sum((X, X))

    # Works in Python 2, but not 3.
    # In Python 3, list comprehensions were given their own scope.
    try:
        Z1 = sum([X for _ in range(3)])
    except NameError:
        Z1 = None

    # Fails in both.
    # Apparently, generator expressions (that's what the entire argument
    # to sum() is) did have their own scope even in Python 2.
    try:
        Z2 = sum(X for _ in range(3))
    except NameError:
        Z2 = None

    # Workaround: put the computation in lambda or def.
    compute_z3 = lambda val: sum(val for _ in range(3))

    # Then use that function.
    Z3 = compute_z3(X)

    # Also worth noting: here I can refer to XS in the for-part of the
    # generator expression (Z4 works), but I cannot refer to XS in the
    # inner-part of the generator expression (Z5 fails).
    XS = [15, 15, 15, 15]
    Z4 = sum(val for val in XS)
    try:
        Z5 = sum(XS[i] for i in range(len(XS)))
    except NameError:
        Z5 = None

print(Foo.Z1, Foo.Z2, Foo.Z3, Foo.Z4, Foo.Z5)

2

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

এটি কেন ত্রুটিপূর্ণ তা বোঝাতে, আসল উদাহরণে ফিরে আসি। এটি ব্যর্থ:

class Foo:
    x = 5
    y = [x for i in range(1)]

তবে এটি কাজ করে:

def Foo():
    x = 5
    y = [x for i in range(1)]

সীমাবদ্ধতাটি রেফারেন্স গাইডে এই বিভাগের শেষে বলা আছে ।


1

যেহেতু বাহ্যতমতম পুনরাবৃত্তিকে আশেপাশের স্কোপটিতে মূল্যায়ন করা হয় আমরা নির্ভরতা বোঝার ক্ষেত্রের উপর নির্ভরতা বহন করতে zipএকসাথে ব্যবহার itertools.repeatকরতে পারি:

import itertools as it

class Foo:
    x = 5
    y = [j for i, j in zip(range(3), it.repeat(x))]

কেউ forবোঝার ক্ষেত্রে নেস্টেড লুপগুলি ব্যবহার করতে পারেন এবং বাইরেরতম পুনরাবৃত্তির মধ্যে নির্ভরতা অন্তর্ভুক্ত করতে পারেন:

class Foo:
    x = 5
    y = [j for j in (x,) for i in range(3)]

ওপির নির্দিষ্ট উদাহরণের জন্য:

from collections import namedtuple
import itertools as it

class StateDatabase:
    State = namedtuple('State', ['name', 'capital'])
    db = [State(*args) for State, args in zip(it.repeat(State), [
        ['Alabama', 'Montgomery'],
        ['Alaska', 'Juneau'],
        # ...
    ])]
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.