Предположим, вы хотите подсчитать количество нажатий кнопки пользователя на веб-странице. Для этого вы вызываете функцию на onclick
событии кнопки, чтобы обновить счетчик переменной
<button onclick="updateClickCount()">click me</button>
1) Вы может использовать глобальную переменную и функцию для увеличения счетчика:
var counter = 0;
function updateClickCount() {
++counter;
// do something with counter
}
Но ошибка заключается в том, что любой скрипт на странице может изменять счетчик, не вызывая updateClickCount()
.
2) Теперь вы можете подумать об объявлении переменной внутри функции:
function updateClickCount() {
var counter = 0;
++counter;
// do something with counter
}
Но, Эй! Каждый раз, когда вызывается функция updateClickCount()
, счетчик снова устанавливается на 1.
3) Размышление о вложенных функциях?
Вложенные функции имеют доступ к области «выше». В этом примере внутренняя функция updateClickCount()
имеет доступ к переменной счетчика в родительской функции countWrapper()
function countWrapper() {
var counter = 0;
function updateClickCount() {
++counter;
// do something with counter
}
updateClickCount();
return counter;
}
. Это могло бы решить дилемму счетчика, если бы вы могли достичь функции updateClickCount()
извне, и вам также нужно найти способ выполнить counter = 0
только один раз не каждый раз.
4) Закрытие для спасения! (функция самозапуска):
var updateClickCount=(function(){
var counter=0;
return function(){
++counter;
// do something with counter
}
})();
Функция самозапуска только запускается один раз. Он устанавливает counter
в ноль (0) и возвращает выражение функции.
Таким образом updateClickCount
становится функцией. «Замечательная» часть состоит в том, что она может обращаться к счетчику в родительском пространстве.
Это называется закрытием JavaScript. Это дает возможность для функции иметь переменные [ private .
counter
защищен областью анонимной функции и может быть изменен только с помощью добавления функция!
Более яркий пример закрытия:
<script>
var updateClickCount=(function(){
var counter=0;
return function(){
++counter;
document.getElementById("spnCount").innerHTML=counter;
}
})();
</script>
<html>
<button onclick="updateClickCount()">click me</button>
<div> you've clicked
<span id="spnCount"> 0 </span> times!
</div>
</html>
Ответ Нади довольно полный - это практически не то, чем вы хотите заниматься; вы уверены, что не можете использовать наследование в WidgetTypes
вместо вложенных классов?
Единственная причина использовать вложенные классы - это инкапсулировать классы, тесно взаимодействующие друг с другом, ваш конкретный пример для меня выглядит как непосредственный кандидат на наследование - нет никакой пользы от вложенности классов WidgetType
вместе; поместите их в модуль и вместо этого унаследуйте от базового WidgetType
.
Модуль пикулирования пытается получить от модуля класс TextType. Но так как класс вложен, то это не работает. Предложение Джейсона сработает. Вот строки в pickle.py, отвечающие за сообщение об ошибке:
try:
__import__(module)
mod = sys.modules[module]
klass = getattr(mod, name)
except (ImportError, KeyError, AttributeError):
raise PicklingError(
"Can't pickle %r: it's not found as %s.%s" %
(obj, module, name))
klass = getattr(mod, name)
конечно же не сработает в случае вложенного класса. Чтобы продемонстрировать, что происходит, попробуйте добавить эти строки перед пикировкой экземпляра:
import sys
setattr(sys.modules[__name__], 'TextType', WidgetType.TextType)
Этот код добавляет TextType в качестве атрибута к модулю. Пикировка должна работать просто отлично. Но я не советую использовать этот хак.
.Pickle работает только с классами, определенными в области видимости модуля (верхний уровень). В данном случае, похоже, что можно определить вложенные классы в области видимости модуля, а затем установить их в качестве свойств на WidgetType, предполагая, что есть причина не просто ссылаться в своем коде на TextType
и FloatType
. Или импортируйте модуль, в котором они находятся и используйте widget_type.TextType
и widget_type.FloatType
.
В Sage (www.sagemath.org) мы имеем много примеров этой проблемы pickling. Мы решили систематически решить ее следующим образом: поместить внешний класс в специальный метакласс, целью которого является реализация и скрытие хака. Обратите внимание, что это автоматически распространяется через вложенные классы, если есть несколько уровней вложенности.