كيفية وضع المتغيرات على المكدس/السياق في بايثون

StackOverflow https://stackoverflow.com/questions/1001784

سؤال

في جوهر, أريد أن أضع متغير على المكدس ، التي سيتم الوصول إليها من قبل جميع المكالمات أقل من ذلك الجزء على المكدس حتى كتلة مخارج.في جافا أود أن حل هذه باستخدام ثابت الخيط المحلية مع دعم الأساليب التي ثم يمكن الوصول إليها من الطرق.

مثال نموذجي:يمكنك الحصول على طلب فتح اتصال قاعدة البيانات.حتى اكتمال الطلب ، تريد كل رمز إلى استخدام اتصال قاعدة البيانات هذه.بعد الانتهاء من وإغلاق طلب إغلاق اتصال قاعدة البيانات.

ما أحتاج هذا هو مولد تقرير.كل تقرير تتكون من عدة أجزاء ، كل جزء يمكن أن تعتمد على عمليات حسابية مختلفة ، وأحيانا أجزاء مختلفة تعتمد في جزء منها على نفس الحساب.وأنا لا أريد أن أكرر الثقيلة الحسابات ، أحتاج إلى ذاكرة التخزين المؤقت لهم.فكرتي هي لتزيين الطرق مع ذاكرة التخزين المؤقت الديكور.ذاكرة التخزين المؤقت يخلق هوية تقوم على أسلوب اسم وحدة ، ومن حجج تبدو إذا كان لديه هذا allready قد تحسب في كومة متغير, و ينفذ الأسلوب إن لم يكن.

سأحاول clearify من خلال عرض بلدي الحالي التنفيذ.أريد أريد القيام به هو تبسيط رمز لأولئك تنفيذ العمليات الحسابية.

أولا, يجب المركزي الوصول إلى ذاكرة التخزين المؤقت الكائن الذي أسميه MathContext:

class MathContext(object):
    def __init__(self, fn): 
        self.fn = fn
        self.cache = dict()
    def get(self, calc_config):
        id = create_id(calc_config)
        if id not in self.cache:
            self.cache[id] = calc_config.exec(self)
        return self.cache[id]

الجبهة الوطنية الحجة هو اسم الملف سياق يتم إنشاؤه في ما يتعلق ، من حيث البيانات يمكن قراءتها أن تكون محسوبة.

ثم لدينا حساب الدرجة:

 class CalcBase(object):
     def exec(self, math_context):
         raise NotImplementedError

و هنا هو غبي فيبوناتشي سبيل المثال.غير أساليب فعلا العودية ، فإنها تعمل على مجموعات كبيرة من البيانات بدلا من ذلك ، ولكنه يعمل على إظهار كيف يمكنك أن تعتمد على غيرها من الحسابات:

class Fibonacci(CalcBase):
    def __init__(self, n): self.n = n
    def exec(self, math_context):
        if self.n < 2: return 1
        a = math_context.get(Fibonacci(self.n-1))
        b = math_context.get(Fibonacci(self.n-2))
        return a+b

ما أريد فيبوناتشي أن يكون بدلا من ذلك ، هو مجرد تزيين الطريقة:

@cache
def fib(n):
    if n<2: return 1
    return fib(n-1)+fib(n-2)

مع math_context سبيل المثال ، عندما math_context يخرج من نطاق, فهل كل ذلك مؤقتا القيم.أريد نفس الشيء بالنسبة الديكور.Ie.في نقطة X, كل شيء مؤقتا بواسطة @ذاكرة التخزين المؤقت dereferrenced أن يكون التعليم لمواطنة عالمية.

هل كانت مفيدة؟

المحلول

وذهبت إلى الأمام وجعل شيء من الممكن أن تفعل ما تريد. ويمكن استخدامه على حد سواء الديكور ومدير السياق:

from __future__ import with_statement
try:
    import cPickle as pickle
except ImportError:
    import pickle


class cached(object):
    """Decorator/context manager for caching function call results.
    All results are cached in one dictionary that is shared by all cached
    functions.

    To use this as a decorator:
        @cached
        def function(...):
            ...

    The results returned by a decorated function are not cleared from the
    cache until decorated_function.clear_my_cache() or cached.clear_cache()
    is called

    To use this as a context manager:

        with cached(function) as function:
            ...
            function(...)
            ...

    The function's return values will be cleared from the cache when the
    with block ends

    To clear all cached results, call the cached.clear_cache() class method
    """

    _CACHE = {}

    def __init__(self, fn):
        self._fn = fn

    def __call__(self, *args, **kwds):
        key = self._cache_key(*args, **kwds)
        function_cache = self._CACHE.setdefault(self._fn, {})
        try:
            return function_cache[key]
        except KeyError:
            function_cache[key] = result = self._fn(*args, **kwds)
            return result

    def clear_my_cache(self):
        """Clear the cache for a decorated function
        """
        try:
            del self._CACHE[self._fn]
        except KeyError:
            pass # no cached results

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self.clear_my_cache()

    def _cache_key(self, *args, **kwds):
        """Create a cache key for the given positional and keyword
        arguments. pickle.dumps() is used because there could be
        unhashable objects in the arguments, but passing them to 
        pickle.dumps() will result in a string, which is always hashable.

        I used this to make the cached class as generic as possible. Depending
        on your requirements, other key generating techniques may be more
        efficient
        """
        return pickle.dumps((args, sorted(kwds.items())), pickle.HIGHEST_PROTOCOL)

    @classmethod
    def clear_cache(cls):
        """Clear everything from all functions from the cache
        """
        cls._CACHE = {}


if __name__ == '__main__':
    # used as decorator
    @cached
    def fibonacci(n):
        print "calculating fibonacci(%d)" % n
        if n == 0:
            return 0
        if n == 1:
            return 1
        return fibonacci(n - 1) + fibonacci(n - 2)

    for n in xrange(10):
        print 'fibonacci(%d) = %d' % (n, fibonacci(n))


    def lucas(n):
        print "calculating lucas(%d)" % n
        if n == 0:
            return 2
        if n == 1:
            return 1
        return lucas(n - 1) + lucas(n - 2)

    # used as context manager
    with cached(lucas) as lucas:
        for i in xrange(10):
            print 'lucas(%d) = %d' % (i, lucas(i))

    for n in xrange(9, -1, -1):
        print 'fibonacci(%d) = %d' % (n, fibonacci(n))

    cached.clear_cache()

    for n in xrange(9, -1, -1):
        print 'fibonacci(%d) = %d' % (n, fibonacci(n))

نصائح أخرى

هذا السؤال يبدو أن اثنين من السؤال

  • أ) تقاسم اتصال db
  • ب) التخزين المؤقت/Memoizing

ب) كنت قد أجبت على أنفسكم

أ) لا يبدو أن نفهم لماذا تحتاج إلى وضعها على كومة ؟ يمكنك أن تفعل واحدة من هذه

  1. يمكنك استخدام فئة اتصال يمكن أن تكون سمة من ذلك
  2. يمكنك تزيين كل وظيفة الخاص بك بحيث تحصل على اتصال من موقع مركزي
  3. كل وظيفة يمكن استخدام صراحة العالمية طريقة الاتصال
  4. يمكنك إنشاء اتصال و تمر حوله, أو خلق سياق موضوع تمرير حول السياق اتصال يمكن أن تكون جزءا من السياق

إلخ, إلخ

هل يمكن استخدام متغير عمومي ملفوفة في وظيفة جالبة:

def getConnection():
    global connection
    if connection:
        return connection
    connection=createConnection()
    return connection

"يمكنك الحصول على طلب فتح اتصال قاعدة البيانات....إغلاق اتصال قاعدة البيانات."

هذا هو ما هي الأشياء بالنسبة.إنشاء كائن اتصال ، تمريرها إلى كائنات أخرى ، ثم إغلاقه عند الانتهاء من ذلك.Globals ليست مناسبة.ببساطة تمرير قيمة حول كمعلمة إلى غيرها من الأشياء التي يقومون بهذا العمل.

"كل تقرير تتكون من عدة أجزاء ، كل جزء يمكن أن تعتمد على عمليات حسابية مختلفة ، وأحيانا أجزاء مختلفة تعتمد في جزء منها على نفس الحساب....أحتاج إلى ذاكرة التخزين المؤقت لهم"

هذا هو ما هي الأشياء بالنسبة.إنشاء قاموس مفيدة حساب النتائج و مررها من التقرير جزء إلى جزء تقرير.

أنت لا تحتاج إلى الفوضى مع "كومة المتغيرات", "ثابت الخيط المحلية" أو شيء من هذا القبيل.فقط تمرير العادية متغير الحجج العادية طريقة وظائف.عليك أن تكون أكثر سعادة.


class MemoizedCalculation( object ):
    pass

class Fibonacci( MemoizedCalculation ):
    def __init__( self ):
       self.cache= { 0: 1, 1: 1 }
    def __call__( self, arg ):
       if arg not in self.cache:
           self.cache[arg]= self(arg-1) + self(arg-2)
       return self.cache[arg]

class MathContext( object ):
    def __init__( self ):
        self.fibonacci = Fibonacci()

يمكنك استخدامه مثل هذا

>>> mc= MathContext()
>>> mc.fibonacci( 4 )
5

يمكنك تحديد أي عدد من الحسابات أضعاف كل منهم في وعاء واحد كائن.

إذا كنت تريد, يمكنك جعل MathContext في السياق الرسمي مدير بحيث تعمل مع مع البيان.إضافة هاتين الطريقتين إلى MathContext.

def __enter__( self ):
    print "Initialize"
    return self
def __exit__( self, type_, value, traceback ):
    print "Release"

ثم يمكنك أن تفعل هذا.

with  MathContext() as mc:
    print mc.fibonacci( 4 )

في نهاية مع البيان, يمكنك ضمان أن __exit__ الأسلوب كان يسمى.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top