Декоратор Python 3.0 и аргументы декоратору

Проблема с вашим кодом заключается в том, как вы используете AsyncTask, потому что когда вы поворачиваете экран во время спящего потока:

Thread.sleep(2000) 

AsyncTask все еще работает, это потому, что вы не сделали t правильно отменить экземпляр AsyncTask в onDestroy () до перестройки фрагмента (при вращении) и при запуске этого же экземпляра AsyncTask (после вращения) с помощью onPostExecute (), он пытается найти ресурсы с помощью getResources () со старым экземпляром фрагмента ( недопустимый экземпляр):

getResources().getString(R.string.app_name)

, что эквивалентно:

MyFragment.this.getResources().getString(R.string.app_name)

Таким образом, окончательное решение заключается в управлении экземпляром AsyncTask (для отмены, если он все еще работает) до перестройки фрагмента когда вы поворачиваете экран и отменены во время перехода, перезапустите AsyncTask после реконструкции с помощью логического флага:

public class MyFragment extends SherlockFragment {

    private MyAsyncTask myAsyncTask = null;
    private boolean myAsyncTaskIsRunning = true;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(savedInstanceState!=null) {
            myAsyncTaskIsRunning = savedInstanceState.getBoolean("myAsyncTaskIsRunning");
        }
        if(myAsyncTaskIsRunning) {
            myAsyncTask = new MyAsyncTask();
            myAsyncTask.execute();
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean("myAsyncTaskIsRunning",myAsyncTaskIsRunning);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if(myAsyncTask!=null) myAsyncTask.cancel(true);
        myAsyncTask = null;

    }

    public class MyAsyncTask extends AsyncTask<Void, Void, Void>() {

        public MyAsyncTask(){}

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            myAsyncTaskIsRunning = true;
        }
        @Override
        protected Void doInBackground(Void... params) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException ex) {}
            return null;
        }

        @Override
        protected void onPostExecute(Void result){
            getResources().getString(R.string.app_name);
            myAsyncTaskIsRunning = false;
            myAsyncTask = null;
        }

    }
}
6
задан YGA 29 June 2009 в 20:00
поделиться

2 ответа

В этом случае вам нужно заставить вашу функцию возвращать декоратор. (Все может быть решено с помощью другого уровня косвенного обращения ...)

from decorator import decorator
def substitute_args(arg_sub_dict):
  @decorator
  def wrapper(fun, arg):
    new_arg = arg_sub_dict.get(arg, arg)
    return fun(new_arg)
  return wrapper

Это означает, что substitute_args не сам декоратор, это фабрика декораторов . Вот эквивалент без модуля декоратора .

def substitute_args(arg_sub_dict):
  def my_decorator(fun):
    def wrapper(arg):
      new_arg = arg_sub_dict.get(arg, arg)
      return fun(new_arg)
    # magic to update __name__, etc.
    return wrapper
  return my_decorator

Три уровня в глубину не очень удобно, но помните, что два из них - это когда функция определена:

@substitute_args({}) # this function is called and return value is the decorator
def f(x):
  return x
# that (anonymous) decorator is applied to f

Что эквивалентно:

def f(x):
  return x
f = substitude_args({})(f) # notice the double call
8
ответ дан 17 December 2019 в 02:32
поделиться

Вот еще один способ, которым я только что обнаружил: проверьте, требуется ли первый (и только) аргумент для вашего декоратора; Если это так, вы сделаете и можете вернуть свой метод модификации поведения обертки (сам украшен Functools.wraws , чтобы сохранить название и строку документации).

В другом случае следует присутствовать один или несколько именованных или позиционных аргументов; Вы можете собрать эти аргументы и вернуть Callable, который принимает Callable в качестве первого аргумента и возвращает метод обертки - и с тех пор описание соответствует описанию метода декоратора, верните этот метод очень декоратора! Я использовал functools.partial Здесь, чтобы получить версию своего декоратора, IS_GLOBAL_Method (который я работаю прямо сейчас - его реализация, конечно, чепуха, как показано ниже, это только продемонстрировать декоративные работы).

Это решение, по-видимому, работает, но наверняка необходима больше тестирования. Если вы увидите наших глаз, вы можете увидеть, что трюк - всего три или четыре строки, чтобы помнить шаблон. Теперь мне интересно, могу ли я обернуть такого рода функциональность в другой декоратор? Ах, метанесса этого!

from functools import wraps
from functools import partial

_               = print
is_instance_of  = isinstance
is_callable     = lambda x: hasattr( x, '__call__' )

def is_global_method( x, *, name = None ):
  if is_callable( x ):
    @wraps( x )
    def wrapper( *P, **Q ):
      return { 'name': name, 'result': x( *P, **Q ), }
    return wrapper
  # assert is_instance_of( x, str ) # could do some sanity checks here
  return partial( is_global_method, name = x )

@is_global_method
def f( x ):
  """This is method f."""
  return x ** 2

@is_global_method( 'foobar' )
def g( x ):
  """This is method g."""
  return x ** 2

_( f.__name__ )
_( f.__doc__ )
_( f( 42 ) )
_( g.__name__ )
_( g.__doc__ )
_( g( 42 ) )
-2
ответ дан 17 December 2019 в 02:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: