একটি ফাংশনের অভ্যন্তরে স্থির পরিবর্তনকের পাইথন সমতুল্য কী?


630

এই সি / সি ++ কোডটির আইডিয়োমেটিক পাইথনের সমতুল্য কী?

void foo()
{
    static int counter = 0;
    counter++;
    printf("counter is %d\n", counter);
}

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


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

8
নন-সি-প্রোগ্রামারদের জন্য, [ ফাংশনের অভ্যন্তরে স্থির পরিবর্তনশীল কেবল সেই ফাংশনের স্কোপের ভিতরেই দৃশ্যমান হয় তবে তার জীবনকালটি প্রোগ্রামটির পুরো জীবন, এবং এটি কেবল একবারই শুরু হয়েছিল)। মূলত, একটি ধ্রুবক কাউন্টার বা স্টোরেজ ভেরিয়েবল যা ফাংশন কলগুলির মধ্যে থাকে।
স্মি

2
@ পিএলএপএপ: ধরণের রয়েছে, এটি একটি শ্রেণির সদস্য । আপনি সঠিক যে আমরা অন্যান্য কোড এটি দেখতে বা এটি পরিবর্তন করতে বাধা দিতে পারি না।
স্মি

উত্তর:


681

কিছুটা বিপরীত, তবে এটি কাজ করা উচিত:

def foo():
    foo.counter += 1
    print "Counter is %d" % foo.counter
foo.counter = 0

আপনি যদি নীচের পরিবর্তে উপরের দিকে কাউন্টার ইনিশিয়াল কোডটি চান তবে আপনি একটি ডেকরেটর তৈরি করতে পারেন:

def static_vars(**kwargs):
    def decorate(func):
        for k in kwargs:
            setattr(func, k, kwargs[k])
        return func
    return decorate

তারপরে কোডটি ব্যবহার করুন:

@static_vars(counter=0)
def foo():
    foo.counter += 1
    print "Counter is %d" % foo.counter

foo.দুর্ভাগ্যক্রমে এটির জন্য আপনার উপসর্গটি ব্যবহার করা দরকার ।

(ক্রেডিট: @ একাই )


23
ফু এর কেবল একটি উদাহরণ রয়েছে - এটি একটি ফাংশন। সমস্ত অনুরোধ একই পরিবর্তনশীল অ্যাক্সেস।
ক্লাদিউ

121
এটি খনন করার জন্য দুঃখিত, তবে আমি এটির if "counter" not in foo.__dict__: foo.counter = 0প্রথম লাইন হিসাবে রাখি foo()। এটি ফাংশনের বাইরে কোড এড়াতে সহায়তা করবে। যদিও এটি 2008 সালে ফিরে সম্ভব ছিল কিনা তা নিশ্চিত নয়। স্থিতিশীল ফাংশন ভেরিয়েবলগুলি তৈরি করার সম্ভাবনার সন্ধান করার সময় পিএস এই উত্তরটি
পেয়েছিল

8
@ বাইনারিএলভি: আমি সম্ভবত এটি প্রথম পদ্ধতির চেয়ে পছন্দ করব। প্রথম পদ্ধতির সমস্যাটি হ'ল এটি তাত্ক্ষণিকভাবে স্পষ্ট নয় fooএবং foo.counter = ঘনিষ্ঠভাবে সম্পর্কিত। যাইহোক, আমি শেষ পর্যন্ত সাজসজ্জার পদ্ধতির পছন্দ করি, কারণ কোনওভাবেই সাজসজ্জাকারীকে ডাকা হবে না এবং এটি কী করে তা শব্দার্থগতভাবে আরও স্পষ্টভাবে বোঝা যায় ( বিশেষত পরবর্তীকালে যেমন আপনি ব্যবহার করতে চান তার @static_var("counter", 0)চেয়ে সহজ এবং আমার চোখে আরও বুদ্ধিমান হয়) if "counter" not in foo.__dict__: foo.counter = 0ফাংশনের নাম (দ্বিগুণ) যা পরিবর্তিত হতে পারে)।
ক্লাদিউ

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

3
def foo(): if not hasattr(foo,"counter"): foo.counter=0 foo.counter += 1
এরিক অ্যারোনস্টি

221

আপনি কোনও ফাংশনে বৈশিষ্ট্য যুক্ত করতে পারেন এবং এটি স্ট্যাটিক ভেরিয়েবল হিসাবে ব্যবহার করতে পারেন।

def myfunc():
  myfunc.counter += 1
  print myfunc.counter

# attribute must be initialized
myfunc.counter = 0

বিকল্পভাবে, আপনি যদি ফাংশনের বাইরে ভেরিয়েবল সেটআপ করতে না চান তবে আপনি hasattr()একটি AttributeErrorব্যতিক্রম এড়াতে ব্যবহার করতে পারেন :

def myfunc():
  if not hasattr(myfunc, "counter"):
     myfunc.counter = 0  # it doesn't exist yet, so initialize it
  myfunc.counter += 1

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


6
যদি বিবৃতি না দিয়ে চেষ্টা করবেন না কেন?
রাভউজডিলা

12
try: myfunc.counter += 1; except AttributeError: myfunc.counter = 1পরিবর্তে ব্যতিক্রমগুলি ব্যবহার করে একই কাজ করা উচিত।
sleblanc

ব্যতিক্রমগুলি ব্যতিক্রমী পরিস্থিতিতে ব্যবহার করা উচিত, যেমন প্রোগ্রামার প্রত্যাশা করে না, যেমন একটি ইনপুট ফাইল যা এটি হ'ল আকস্মিকভাবে খোলেছিল হঠাৎ উপলভ্য নয়। এটি একটি প্রত্যাশিত পরিস্থিতি, যদি একটি বিবৃতি আরও অর্থবোধ করে।
হ্যাক 7

11
@ হ্যাক_সো: আচ্ছা, এটি পাইথোনিক (অনুমতি চেয়ে ক্ষমা চাইতে আরও ভাল)। পাইথন অপ্টিমাইজেশান কৌশলগুলিতে এটি প্রস্তাবিত কারণ এটি যদি কোনও ব্যয় বাঁচায় (যদিও আমি অকাল অপ্টিমাইজেশনের প্রস্তাব দিই না)। ব্যতিক্রমী মামলা সম্পর্কে আপনার নিয়ম: 1. ব্যর্থতা এখানে এক অর্থে ব্যতিক্রমী কেস। এটি কেবল একবারই ঘটে। ২. আমি মনে করি যে বিধিটি ব্যতিক্রমগুলি (অর্থাত্ উত্থাপন) ব্যবহার সম্পর্কে। এটি আপনার কাজ করা প্রত্যাশিত কিছুটির জন্য ব্যতিক্রমী হয়ে উঠছে তবে এর জন্য ব্যাকআপ পরিকল্পনা রয়েছে যা বেশিরভাগ ভাষায় একটি সাধারণ বিষয়।
লিউজ

@ লেইয়াংঝং: কি এমন কোনও ব্লক সংযুক্তি রাখলে যা tryব্যয় বাড়িয়ে তুলবে না ? উৎসুক.
trss

201

যে কেউ বিবেচনা করতে পারে:

def foo():
    try:
        foo.counter += 1
    except AttributeError:
        foo.counter = 1

রিজনিং:

  • অনেক অজগর ("ক্ষমা প্রার্থনার অনুমতি চাই না")
  • ifশাখার পরিবর্তে ব্যতিক্রম (কেবল একবার নিক্ষিপ্ত) ব্যবহার করুন ( স্টপআইটারেশন ব্যতিক্রম মনে করুন )

11
আমি পাইথন দীর্ঘকাল যাচ্ছিলাম না, তবে এটি ভাষার অন্তর্নিহিত টিনেন্টগুলির একটিকে সন্তুষ্ট করে: যদি এটি (মোটামুটি) সহজ না হয় তবে আপনি এটি ভুল করছেন
জেডএক্স 9

শ্রেণীবদ্ধ পদ্ধতিতে তত্ক্ষণাত্ কাজ করেনি, "self.foo.counter = 1" আবার অ্যাট্রিবিউটআরার উত্থাপন করে।
ভিলাসভ

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

3
একটি সহজ পদ্ধতির: def fn(): if not hasattr(fn, 'c'): fn.c = 0 fn.c += 1 return fn.c
থ্রিকরিয়াস ওয়ান

5
@ মনু এর hasattr()জন্য ব্যবহার করা সহজ এবং কম দক্ষ নয়।
moooeeeep

48

অন্যান্য উত্তরগুলি আপনার এইভাবে করা উচিত demonst এখানে এমন একটি উপায় যা আপনার করা উচিত নয়:

>>> def foo(counter=[0]):
...   counter[0] += 1
...   print("Counter is %i." % counter[0]);
... 
>>> foo()
Counter is 1.
>>> foo()
Counter is 2.
>>> 

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


আমি চেষ্টা করেছিলাম, তবে কোনও কারণে, ফাংশন প্যারামিটারটি নিজেকে 0 থেকে শুরু করে 140 না দিয়ে শুরু করা হয়েছিল, কেন এটি হবে?
অ্যান্ড্রুডটনিচ

1
@ বাউভার্ড পুনরাবৃত্ত ফাংশনগুলির জন্য যা স্ট্যাটিক ভেরিয়েবলের প্রয়োজন, এটিই কেবলমাত্র সত্যই ভাল পড়ে reads
লাইফবেলেন্স

1
আমি বেশ কয়েকটি পদ্ধতির চেষ্টা করেছি এবং আমি আশা করি এটি একটি অজগর হিসাবে গ্রহণযোগ্য হয়। কিছু অর্থপূর্ণ নাম সহ def foo(arg1, arg2, _localstorage=DataClass(counter=0))আমি এটি ভাল পঠনযোগ্য বলে মনে করি। আরেকটি ভাল পয়েন্ট সহজ ফাংশন নামকরণ।
ভিপিএফবি

2
আপনি কেন বলেন যে এটি করা উচিত নয়? আমার কাছে পুরোপুরি যুক্তিযুক্ত মনে হচ্ছে!
কনস্ট্যান্টিন

1
@ ভিপিএফবি: সাধারণ সঞ্চয়স্থানের জন্য, আপনি কোনও বিশেষ শ্রেণীর সংজ্ঞা types.SimpleNamespaceনা দিয়ে def foo(arg1, arg2, _staticstorage=types.SimpleNamespace(counter=0)):এটি তৈরি করতে পারবেন ।
শ্যাডোর্যাঞ্জার

43

অনেকে ইতিমধ্যে 'হ্যাশটার' পরীক্ষার পরামর্শ দিয়েছেন, তবে এর সহজ উত্তর রয়েছে:

def func():
    func.counter = getattr(func, 'counter', 0) + 1

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


2
আপনি যখন সেখানে একটি ফানক রেখেছেন তখন গেটিয়েটারের তৃতীয় পারমটির দিকে মনোযোগ দিন উদাহরণস্বরূপ: ডিএফ ফানক (): ডিফ ফু (): রিটার্ন 1112 ফানক্যাক্টর = গ্যাটট্রা (ফানক, 'কাউন্টার', ফু ()) + 1 আপনি কল করলে ফানক, ফু সবসময় বলা হবে!
কোডফোর

1
প্রতিবার যখন ফানক কল আসে তখন গেটআটারের জন্য কেবল একটি কল। পারফরম্যান্স যদি কোনও সমস্যা না হয় তবে এটি চেষ্টা করুন / বাদে হাতছাড়া করবেন।
মার্ক লরেন্স

2
@ মার্কলওরেন্স: আসলে আমার উইন্ডোজ এক্স 64৪.৮.০ ইনস্টল করার সময়, এই উত্তর এবং রাভওজডিলার সমতুল্য try/ exceptভিত্তিক পদ্ধতির মধ্যে পারফরম্যান্স পার্থক্যটি বেশ অর্থহীন। একটি সরল ipython %%timeitmicrobenchmark খরচ দিয়েছিলেন try/ except, কল প্রতি 255 NS এ বনাম জন্য 263 NS getattrভিত্তিক সমাধান। হ্যাঁ, try/ exceptদ্রুততর, তবে এটি ঠিক "হাতছাড়া করা" নয়; এটি একটি ক্ষুদ্র মাইক্রো-অপ্টিমাইজেশন। কোড যেটি পরিষ্কার বলে মনে হয় তা লিখুন, তুচ্ছ পারফরম্যান্সের পার্থক্যের বিষয়ে চিন্তা করবেন না।
শ্যাডোর্যাঞ্জার

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

28

এখানে একটি সম্পূর্ণ এনপ্যাপুলেটেড সংস্করণ রয়েছে যার জন্য বাহ্যিক প্রারম্ভিককরণ কলটির প্রয়োজন নেই:

def fn():
    fn.counter=vars(fn).setdefault('counter',-1)
    fn.counter+=1
    print (fn.counter)

পাইথনে, ফাংশনগুলি হ'ল বস্তু এবং আমরা বিশেষ বৈশিষ্ট্যের মাধ্যমে তাদের মধ্যে সহজভাবে বাঁদর প্যাচ, সদস্য ভেরিয়েবল যুক্ত করতে পারি __dict__। অন্তর্নির্মিত vars()বিশেষ বৈশিষ্ট্যটি প্রদান করে __dict__

সম্পাদনা: দ্রষ্টব্য, বিকল্প try:except AttributeErrorউত্তর থেকে পৃথক, এই পদ্ধতির সাথে পরিবর্তনশীল সর্বদা প্রাথমিক সূচনা নিম্নলিখিত কোড যুক্তি জন্য প্রস্তুত থাকবে। আমি মনে করি try:except AttributeErrorনিম্নলিখিতগুলির বিকল্পটি কম ডিআরওয়াই হবে এবং / বা বিশ্রী প্রবাহ থাকবে:

def Fibonacci(n):
   if n<2: return n
   Fibonacci.memo=vars(Fibonacci).setdefault('memo',{}) # use static variable to hold a results cache
   return Fibonacci.memo.setdefault(n,Fibonacci(n-1)+Fibonacci(n-2)) # lookup result in cache, if not available then calculate and store it

সম্পাদনা 2: আমি কেবলমাত্র উপরের পদ্ধতির প্রস্তাব দিই যখন একাধিক অবস্থান থেকে ফাংশনটি ডাকা হবে। পরিবর্তে যদি ফাংশনটি কেবলমাত্র এক জায়গায় ডাকা হয় তবে এটি ব্যবহার করা ভাল nonlocal:

def TheOnlyPlaceStaticFunctionIsCalled():
    memo={}
    def Fibonacci(n):
       nonlocal memo  # required in Python3. Python2 can see memo
       if n<2: return n
       return memo.setdefault(n,Fibonacci(n-1)+Fibonacci(n-2))
    ...
    print (Fibonacci(200))
    ...

2
এটির সাথে একমাত্র সমস্যাটি হ'ল এটি একেবারেই ঝরঝরে নয় এবং যখনই আপনি এই প্যাটার্নটি ব্যবহার করতে চান আপনাকে কোডটি কাটাতে হবে এবং পেস্ট করতে হবে ... সুতরাং আমার সাজসজ্জার ব্যবহার
ক্লাদিউ

2
সম্ভবত ভালো কিছু ব্যবহার করা উচিতtry: mystaticfun.counter+=10 except AttributeError: mystaticfun.counter=0
endolith

2
দয়া করে X not in Yপরিবর্তে ব্যবহার করুন not X in Y(বা আপনি যদি এটির সাথে আরও সাদৃশ্যযুক্ত তুলনার জন্য কেবল এটি ব্যবহার করে থাকেন তবে ব্যবহার করার পরামর্শ দিন hasattr)
নিক

এটি সম্পর্কে কীভাবে: def fn(): if not hasattr(fn, 'c'): fn.c = 0 fn.c += 1 return fn.c
থ্রিকরিয়াস ওয়ান

এটি আদর্শ নয় কারণ যদি আইফোনটি অপ্রয়োজনীয় বাসা বাঁধে, এই পরিস্থিতিতে আমি সেটডিফল্ট পছন্দ করি
রিয়াজ রিজভী

27

পাইথনের স্ট্যাটিক ভেরিয়েবল নেই তবে আপনি কলযোগ্য শ্রেণীর অবজেক্টটি সংজ্ঞায়িত করে এবং পরে এটি একটি ফাংশন হিসাবে ব্যবহার করে এটি জাল করতে পারেন। এই উত্তরটি দেখুন

class Foo(object):
  # Class variable, shared by all instances of this class
  counter = 0

  def __call__(self):
    Foo.counter += 1
    print Foo.counter

# Create an object instance of class "Foo," called "foo"
foo = Foo()

# Make calls to the "__call__" method, via the object's name itself
foo() #prints 1
foo() #prints 2
foo() #prints 3

নোট করুন যে __call__কোনও শ্রেণীর উদাহরণ (বস্তু) তার নিজের নামে কলযোগ্য। এজন্য foo()উপরে কল করা ক্লাসকে ' __call__পদ্ধতিতে কল করে । ডকুমেন্টেশন থেকে :

স্বেচ্ছাসেবী শ্রেণীর উদাহরণগুলি __call__()তাদের শ্রেণিতে একটি পদ্ধতি সংজ্ঞায়িত করে কলযোগ্য হতে পারে ।


15
ফাংশনগুলি ইতিমধ্যে অবজেক্টস তাই এটি কেবল একটি অপ্রয়োজনীয় স্তর যুক্ত করে।
দাসিচ

দীর্ঘ এই মতামতের জন্য এই এসও উত্তরটি দেখুন এটি আসলে একটি ভাল ধারণা। stackoverflow.com/questions/460586 । আমি সম্মত হই যে এই জাতীয় কোনও শ্রেণিকে সম্ভবত সিঙ্গলটন বানানো, সম্ভবত এই স্ট্যাকওভারফ্লো.com / প্রশ্নস / 606760০68৮৫ এর মতো , এটিও একটি ভাল ধারণা হবে। আমি জানি না যে @ এস.লট "... ... শ্রেণীর সংজ্ঞায় কাউন্টারে সরান ..." এর অর্থ কী, কারণ দেখে মনে হচ্ছে এটি ইতিমধ্যে আমার কাছে শ্রেণিক-পরিবর্তনশীল অবস্থানে রয়েছে।
রেব.কবিন

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

1
যদি আমি foo1 = Foo () এবং foo2 = Foo () চান তবে কী হবে?
মার্ক লরেন্স

@ মারকলওরেন্স এর পরে আপনার কাছে কলযোগ্য শ্রেণীর দুটি পৃথক উদাহরণ রয়েছে, প্রত্যেকটির নিজস্ব কাউন্টার রয়েছে। আপনি যদি fooসিঙ্গলটন হিসাবে সরবরাহিত দৃষ্টান্তটি ব্যবহার না করে থাকেন তবে আপনার ঠিক কী আশা করা উচিত ।
অ্যারন ম্যাকমিলিন

14

একটি পুনরায় উত্পাদক জেনারেটর ফাংশন ব্যবহার করুন।

def foo_gen():
    n = 0
    while True:
        n+=1
        yield n

তারপরে এটি ব্যবহার করুন

foo = foo_gen().next
for i in range(0,10):
    print foo()

আপনি যদি একটি উচ্চতর সীমা চান:

def foo_gen(limit=100000):
    n = 0
    while n < limit:
       n+=1
       yield n

যদি পুনরুক্তিকারী সমাপ্ত হয় (উপরের উদাহরণের মতো), আপনি সরাসরি এটির মতো লুপও করতে পারেন like

for i in foo_gen(20):
    print i

অবশ্যই, এই সাধারণ ক্ষেত্রে এক্সরেঞ্জ ব্যবহার করা ভাল :)

ফলন বিবরণীতে ডকুমেন্টেশন এখানে ।


11

অন্যান্য সমাধানগুলি ফাংশনটিতে একটি পাল্টা বৈশিষ্ট্য সংযুক্ত করে, সাধারণত সূচনাটি পরিচালনা করার জন্য সংশ্লেষযুক্ত যুক্তি দিয়ে। এটি নতুন কোডের জন্য অনুপযুক্ত।

পাইথন 3-তে সঠিক উপায়টি হল একটি nonlocalবিবৃতি ব্যবহার করা :

counter = 0
def foo():
    nonlocal counter
    counter += 1
    print(f'counter is {counter}')

বিবৃতিটির নির্দিষ্টকরণের জন্য পিইপি 3104 দেখুন nonlocal

যদি কাউন্টারটি মডিউলটিতে ব্যক্তিগত থাকার উদ্দেশ্যে করা হয়, তবে এটির _counterপরিবর্তে নামকরণ করা উচিত ।


পাইথন 3 এর আগেও আপনি সর্বদা এর global counterপরিবর্তে একটি বিবৃতি দিয়ে এটি করতে পারেন nonlocal counter( nonlocalকেবল আপনাকে কোনও নেস্টেড ফাংশনে ক্লোজার অবস্থায় লিখতে দেয়)। লোকেরা এই ফাংশনটির সাথে একটি বৈশিষ্ট্য সংযুক্ত করার কারণটি হ'ল ফাংশনের সাথে সুনির্দিষ্ট রাষ্ট্রের জন্য বিশ্বব্যাপী নেমস্পেসকে দূষিত করা এড়ানো, যাতে দুটি ফাংশনের স্বতন্ত্র প্রয়োজন হলে আপনাকে এমনকি হ্যাকার জিনিসও করতে হবে না counter। এই সমাধান স্কেল হয় না; ফাংশন বৈশিষ্ট্য না। কেডিবি'র উত্তর হ'ল কীভাবে nonlocalসহায়তা করতে পারে তবে এটি জটিলতা যুক্ত করে।
শ্যাডোর্যাঞ্জার

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

8

স্ট্যাটিক ভেরিয়েবল হিসাবে ফাংশনটির একটি অ্যাট্রিবিউট ব্যবহার করার কিছু সম্ভাব্য ত্রুটি রয়েছে:

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

দ্বিতীয় ইস্যুর আইডোমেটিক পাইথন সম্ভবত একটি শীর্ষস্থানীয় আন্ডারস্কোর দিয়ে ভেরিয়েবলের নামকরণ করবে যেটি এটির অ্যাক্সেস করার অর্থ নয়, সত্যের পরে এটি অ্যাক্সেসযোগ্য রেখে keeping

একটি বিকল্প লেজিকাল ক্লোজারগুলি ব্যবহার করে এমন একটি প্যাটার্ন হবে, যা nonlocalপাইথন 3- এ কীওয়ার্ড সহ সমর্থিত ।

def make_counter():
    i = 0
    def counter():
        nonlocal i
        i = i + 1
        return i
    return counter
counter = make_counter()

দুঃখের সাথে আমি এই সমাধানটিকে ডেকরেটারে আবদ্ধ করার কোনও উপায় জানি না।


7
def staticvariables(**variables):
    def decorate(function):
        for variable in variables:
            setattr(function, variable, variables[variable])
        return function
    return decorate

@staticvariables(counter=0, bar=1)
def foo():
    print(foo.counter)
    print(foo.bar)

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


7

কিছুটা বেশি পাঠযোগ্য, তবে আরও ভার্বোজ (পাইথনের জেন: সুস্পষ্টর চেয়ে আরও ভাল):

>>> def func(_static={'counter': 0}):
...     _static['counter'] += 1
...     print _static['counter']
...
>>> func()
1
>>> func()
2
>>>

এটি কীভাবে কাজ করে তার ব্যাখ্যার জন্য এখানে দেখুন ।


এই কোডটি কেন কাজ করে তা আপনি বিশদভাবে বলতে পারেন? দ্বিতীয়টি foo()ফাংশন সংজ্ঞাতে নির্দিষ্ট মানটির সাথে অভিধানটি পুনরায় আরম্ভ করতে হবে (সুতরাং কাউন্টার কীটির সাথে 0 এর মান রয়েছে)। কেন হয় না?
রাফায়েম

3
@ ইরফামেইডেন: ডিফল্ট আর্গুমেন্টগুলি কেবলমাত্র ফাংশনটি সংজ্ঞায়িত হলেই একবার মূল্যায়ন করা হয় এবং প্রতিটি সময় ফাংশন বলা হয় না।
ড্যানিয়েল কে।

6
_উত্তর = 0
Def foo ():
   গ্লোবাল _উত্তর
   _উত্তর + = 1
   মুদ্রণ 'কাউন্টার হয়', _উত্তর

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


4

বেশ কয়েকটি পদ্ধতির চেষ্টা করার পরে আমি @ ওয়ারওয়ারিয়কের উত্তরের একটি উন্নত সংস্করণ ব্যবহার করে শেষ করেছি:

import types

def func(_static=types.SimpleNamespace(counter=0)):
    _static.counter += 1
    print(_static.counter)

3

কথ্য উপায় ব্যবহার করা বর্গ , গুণাবলী আছে করতে পারে। আলাদা না হওয়ার জন্য আপনার যদি উদাহরণগুলির প্রয়োজন হয় তবে একটি সিঙ্গলটন ব্যবহার করুন।

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

অথবা সম্ভবত কোনও জেনারেটর, যদি আপনার ব্যবহারের ধরণটি ফিট করে।


একা একা পুনরাবৃত্তি ফাংশনগুলির জন্য, defaultযুক্তিটি সর্বাধিক মার্জিত।
লাইফবেলেন্স

3

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

@static_var2('seed',0)
def funccounter(statics, add=1):
    statics.seed += add
    return statics.seed

print funccounter()       #1
print funccounter(add=2)  #3
print funccounter()       #4

class ACircle(object):
    @static_var2('seed',0)
    def counter(statics, self, add=1):
        statics.seed += add
        return statics.seed

c = ACircle()
print c.counter()      #1
print c.counter(add=2) #3
print c.counter()      #4
d = ACircle()
print d.counter()      #5
print d.counter(add=2) #7
print d.counter()      #8    

আপনি যদি পছন্দটি পছন্দ করেন তবে বাস্তবায়নটি এখানে:

class StaticMan(object):
    def __init__(self):
        self.__dict__['_d'] = {}

    def __getattr__(self, name):
        return self.__dict__['_d'][name]
    def __getitem__(self, name):
        return self.__dict__['_d'][name]
    def __setattr__(self, name, val):
        self.__dict__['_d'][name] = val
    def __setitem__(self, name, val):
        self.__dict__['_d'][name] = val

def static_var2(name, val):
    def decorator(original):
        if not hasattr(original, ':staticman'):    
            def wrapped(*args, **kwargs):
                return original(getattr(wrapped, ':staticman'), *args, **kwargs)
            setattr(wrapped, ':staticman', StaticMan())
            f = wrapped
        else:
            f = original #already wrapped

        getattr(f, ':staticman')[name] = val
        return f
    return decorator

3

Https://stackoverflow.com/a/279598/916373 এর মতো কলযোগ্য বস্তুর উপর আর একটি (প্রস্তাবিত নয়!) মোচড় দেওয়া , যদি আপনি কোনও ফানকি স্বাক্ষর ব্যবহার করে কিছু মনে করেন না, তা করতে হবে

class foo(object):
    counter = 0;
    @staticmethod
    def __call__():
        foo.counter += 1
        print "counter is %i" % foo.counter

>>> foo()()
counter is 1
>>> foo()()
counter is 2

3

স্থিতিশীল স্থানীয় ভেরিয়েবলযুক্ত কোনও ফাংশন তৈরির পরিবর্তে, আপনি সর্বদা যা "ফাংশন অবজেক্ট" বলা হয় তা তৈরি করতে পারেন এবং এটি একটি স্ট্যান্ডার্ড (অ-স্থিতিশীল) সদস্য ভেরিয়েবল দিতে পারেন।

আপনি যেহেতু সি ++ লিখিত একটি উদাহরণ দিয়েছেন তাই আমি প্রথমে সি ++ এ "ফাংশন অবজেক্ট" কী তা ব্যাখ্যা করব। একটি "ফাংশন অবজেক্ট" হ'ল অতিরিক্ত লোডযুক্ত কোনও শ্রেণি operator()। শ্রেণীর উদাহরণগুলি ফাংশনের মতো আচরণ করবে। উদাহরণস্বরূপ, আপনি কোনও বস্তু (ওভারলোডযুক্ত ) এবং প্রযুক্তিগতভাবে একটি "ফাংশন" না int x = square(5);হলেও লিখতে পারেন । আপনি কোনও শ্রেণি-সামগ্রী দিতে পারে এমন কোনও বৈশিষ্ট্য আপনি কোনও ফাংশন-অবজেক্ট দিতে পারেন।squareoperator()

# C++ function object
class Foo_class {
    private:
        int counter;     
    public:
        Foo_class() {
             counter = 0;
        }
        void operator() () {  
            counter++;
            printf("counter is %d\n", counter);
        }     
   };
   Foo_class foo;

পাইথনে, আমরা operator()পদ্ধতিটির পরিবর্তে নামটি বাদ দিয়ে ওভারলোডও করতে পারি __call__:

এখানে একটি শ্রেণির সংজ্ঞা দেওয়া হল:

class Foo_class:
    def __init__(self): # __init__ is similair to a C++ class constructor
        self.counter = 0
        # self.counter is like a static member
        # variable of a function named "foo"
    def __call__(self): # overload operator()
        self.counter += 1
        print("counter is %d" % self.counter);
foo = Foo_class() # call the constructor

ক্লাসটি ব্যবহৃত হচ্ছে এর একটি উদাহরণ এখানে দেওয়া হয়েছে:

from foo import foo

for i in range(0, 5):
    foo() # function call

কনসোলে মুদ্রিত আউটপুটটি হ'ল:

counter is 1
counter is 2
counter is 3
counter is 4
counter is 5

আপনি যদি নিজের ফাংশনটি ইনপুট আর্গুমেন্টগুলি নিতে চান তবে আপনি সেগুলিতে যুক্ত করতে পারেন __call__:

# FILE: foo.py - - - - - - - - - - - - - - - - - - - - - - - - -

class Foo_class:
    def __init__(self):
        self.counter = 0
    def __call__(self, x, y, z): # overload operator()
        self.counter += 1
        print("counter is %d" % self.counter);
        print("x, y, z, are %d, %d, %d" % (x, y, z));
foo = Foo_class() # call the constructor

# FILE: main.py - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

from foo import foo

for i in range(0, 5):
    foo(7, 8, 9) # function call

# Console Output - - - - - - - - - - - - - - - - - - - - - - - - - - 

counter is 1
x, y, z, are 7, 8, 9
counter is 2
x, y, z, are 7, 8, 9
counter is 3
x, y, z, are 7, 8, 9
counter is 4
x, y, z, are 7, 8, 9
counter is 5
x, y, z, are 7, 8, 9

3

সলিউশন এন + = 1

def foo():
  foo.__dict__.setdefault('count', 0)
  foo.count += 1
  return foo.count

3

একটি বৈশ্বিক ঘোষণা এই কার্যকারিতা সরবরাহ করে। নীচের উদাহরণে (পাইথন 3.5 বা তার চেয়ে বেশি "চ" ব্যবহার করার জন্য), কাউন্টার ভেরিয়েবলটি ফাংশনের বাইরে সংজ্ঞায়িত করা হয়। ফাংশনে এটিকে বৈশ্বিক হিসাবে সংজ্ঞায়িত করা ইঙ্গিত দেয় যে ফাংশনের বাইরের "গ্লোবাল" সংস্করণটি ফাংশনের জন্য উপলব্ধ করা উচিত। সুতরাং প্রতিবার ফাংশনটি চললে এটি ফাংশনের বাইরে এটি সংরক্ষণ করে ফাংশনের বাইরের মানটি সংশোধন করে।

counter = 0

def foo():
    global counter
    counter += 1
    print("counter is {}".format(counter))

foo() #output: "counter is 1"
foo() #output: "counter is 2"
foo() #output: "counter is 3"

সঠিকভাবে ব্যবহার করা হলে এটি একইভাবে কাজ করে। সি-কোডের পার্থক্য হ'ল ওপির সি উদাহরণে, কাউন্টার ভেরিয়েবলটি কেবল ফাংশন দ্বারা স্পর্শ করা যেতে পারে। পাইথনের একটি গ্লোবাল ভেরিয়েবল স্ক্রিপ্টের যে কোনও জায়গায় ব্যবহার বা পরিবর্তন করা যেতে পারে
মর্টেনসিকেল

2

পাইথন পদ্ধতির অভ্যন্তরে স্থির পরিবর্তনশীল vari

class Count:
    def foo(self):
        try: 
            self.foo.__func__.counter += 1
        except AttributeError: 
            self.foo.__func__.counter = 1

        print self.foo.__func__.counter

m = Count()
m.foo()       # 1
m.foo()       # 2
m.foo()       # 3

1

আমি ব্যক্তিগতভাবে সজ্জকারদের থেকে নিম্নলিখিতগুলি পছন্দ করি। প্রতিটি তাদের নিজস্ব।

def staticize(name, factory):
    """Makes a pseudo-static variable in calling function.

    If name `name` exists in calling function, return it. 
    Otherwise, saves return value of `factory()` in 
    name `name` of calling function and return it.

    :param name: name to use to store static object 
    in calling function
    :type name: String
    :param factory: used to initialize name `name` 
    in calling function
    :type factory: function
    :rtype: `type(factory())`

    >>> def steveholt(z):
    ...     a = staticize('a', list)
    ...     a.append(z)
    >>> steveholt.a
    Traceback (most recent call last):
    ...
    AttributeError: 'function' object has no attribute 'a'
    >>> steveholt(1)
    >>> steveholt.a
    [1]
    >>> steveholt('a')
    >>> steveholt.a
    [1, 'a']
    >>> steveholt.a = []
    >>> steveholt.a
    []
    >>> steveholt('zzz')
    >>> steveholt.a
    ['zzz']

    """
    from inspect import stack
    # get scope enclosing calling function
    calling_fn_scope = stack()[2][0]
    # get calling function
    calling_fn_name = stack()[1][3]
    calling_fn = calling_fn_scope.f_locals[calling_fn_name]
    if not hasattr(calling_fn, name):
        setattr(calling_fn, name, factory())
    return getattr(calling_fn, name)

3
দয়া করে বাধা না না, কিন্তু এই সমাধান আমাকে "বড় কোম্পানী শৈলী" :-) একটি বিট মনে করিয়ে দেয় willa.me/2013/11/the-six-most-common-species-of-code.html
Jjc

হ্যাঁ, অ-বহনযোগ্য (সাধারণভাবে স্ট্যাক ম্যানিপুলেশনটি সিপিথন বাস্তবায়ন বিশদ, আপনি পাইপাই, জাইথন, আয়রন পাইথন, কী থাকতে পারে) তে নির্ভর করতে পারেন এমন কিছু নয়, ভঙ্গুর স্ট্যাক ম্যানিপুলেশন, প্রতিটি ব্যবহারের উপর অর্ধ ডজন ফাংশন কল রয়েছে হয় উপায় একটি সহজ চেয়ে ভাল প্রসাধক ... </ সেকেন্ড>
ShadowRanger

1

এই উত্তরটি @ ক্লাডিউর উত্তর তৈরি করে।

আমি যখনই স্থির ভেরিয়েবল অ্যাক্সেস করার ইচ্ছা করি তখনই আমার কোডটি কম স্পষ্ট হয়ে উঠছিল I

যথা, আমার ফাংশন কোডে আমি লিখতে পছন্দ করব:

print(statics.foo)

পরিবর্তে

print(my_function_name.foo)

সুতরাং, আমার সমাধানটি হ'ল:

  1. staticsফাংশনে একটি বৈশিষ্ট্য যুক্ত করুন
  2. ফাংশন স্কোপ এ, স্থানীয় একটি ভেরিয়েবল staticsএকটি উপন্যাস হিসাবে যুক্ত করুনmy_function.statics
from bunch import *

def static_vars(**kwargs):
    def decorate(func):
        statics = Bunch(**kwargs)
        setattr(func, "statics", statics)
        return func
    return decorate

@static_vars(name = "Martin")
def my_function():
    statics = my_function.statics
    print("Hello, {0}".format(statics.name))

মন্তব্য

আমার পদ্ধতিতে নামের একটি শ্রেণি ব্যবহার করা হয়েছে Bunch, এটি একটি অভিধান যা অ্যাট্রিবিউট-স্টাইল অ্যাক্সেসকে সমর্থন করে, একটি লা জাভাস্ক্রিপ্ট ( মূল নিবন্ধটি দেখুন এটি সম্পর্কে , প্রায় 2000)

এটি মাধ্যমে ইনস্টল করা যেতে পারে pip install bunch

এটি এর মতো হাতে লিখিতও হতে পারে:

class Bunch(dict):
    def __init__(self, **kw):
        dict.__init__(self,kw)
        self.__dict__ = self

দ্রষ্টব্য: types.SimpleNamespace(৩.৩ থেকে পাওয়া যায়) এই আচরণটি বাক্সের বাইরে সমর্থন করে (এবং সিপাইথনে সি তে প্রয়োগ করা হয়, সুতরাং এটি যত দ্রুত সম্ভব তা প্রায়)।
ShadowRanger

0

ড্যানিয়েলের উত্তরের ভিত্তিতে (সংযোজন):

class Foo(object): 
    counter = 0  

def __call__(self, inc_value=0):
    Foo.counter += inc_value
    return Foo.counter

foo = Foo()

def use_foo(x,y):
    if(x==5):
        foo(2)
    elif(y==7):
        foo(3)
    if(foo() == 10):
        print("yello")


use_foo(5,1)
use_foo(5,1)
use_foo(1,7)
use_foo(1,7)
use_foo(1,1)

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

স্ট্যাটিক ভেরিয়েবলটি এখনও সুরক্ষিত এবং কেবলমাত্র ব্যবহার_ফু () ফাংশনের পরিধির মধ্যে ব্যবহার করা হয়

এই উদাহরণে, foo () কে ঠিক একইভাবে (সংশ্লিষ্ট সি ++ সমমানের সাথে সম্মত) ফাংশন করুন:

stat_c +=9; // in c++
foo(9)  #python equiv

if(stat_c==10){ //do something}  // c++

if(foo() == 10):      # python equiv
  #add code here      # python equiv       

Output :
yello
yello

ক্লাস ফু যদি একটি সিংগন ক্লাস হিসাবে সীমাবদ্ধভাবে সংজ্ঞায়িত করা হয় তবে তা আদর্শ। এটি এটিকে আরও অজগর করে তুলবে।


-1

অবশ্যই এটি একটি পুরানো প্রশ্ন তবে আমি মনে করি আমি সম্ভবত কিছু আপডেট সরবরাহ করব।

দেখে মনে হচ্ছে পারফরম্যান্স যুক্তিটি অচল। একই টেস্ট স্যুটটি si_nt_ry এবং isInt_re2 এর জন্য একই ফলাফল দেয় বলে মনে হয়। অবশ্যই ফলাফলগুলি পরিবর্তিত হয়, তবে এটি Xeon W3550 সহ কার্নেল 4.3.01 এ অজগর 3.4.4 সহ আমার কম্পিউটারে একটি সেশন। আমি এটি বেশ কয়েকবার চালিয়েছি এবং ফলাফলগুলি একই রকম বলে মনে হচ্ছে। আমি গ্লোবাল রেজেক্সকে ফাংশন স্থিতিতে সরিয়েছি, তবে পারফরম্যান্সের পার্থক্য নগণ্য is

isInt_try: 0.3690
isInt_str: 0.3981
isInt_re: 0.5870
isInt_re2: 0.3632

পারফরম্যান্স সমস্যা ছাড়াই, মনে হচ্ছে চেষ্টা / ধরা সবচেয়ে ভবিষ্যতের- এবং কর্নারকেস-প্রুফ কোড তৈরি করবে তাই সম্ভবত এটিকে ফাংশনে আবদ্ধ করুন


1
আপনি এখানে কি তুলনা করছেন? এটি অন্যান্য উত্তরের মতামতের মতো বলে মনে হচ্ছে তবে এটি কোনটি পরিষ্কার নয় এবং এটি নিজেই প্রশ্নের উত্তর দেয় না।
ShadowRanger
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.