Поскольку никто еще не упомянул об этом, вот начало решения, использующего Nashorn (часть выполнения JavaScript для Java 8).
Решение
private static final String EXTRACTOR_SCRIPT =
"var fun = function(raw) { " +
"var json = JSON.parse(raw); " +
"return [json.pageInfo.pageName, json.pageInfo.pagePic, json.posts[0].post_id];};";
public void run() throws ScriptException, NoSuchMethodException {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(EXTRACTOR_SCRIPT);
Invocable invocable = (Invocable) engine;
JSObject result = (JSObject) invocable.invokeFunction("fun", JSON);
result.values().forEach(e -> System.out.println(e));
}
Сравнение производительности
Я написал контент JSON, содержащий три массива соответственно 20, 20 и 100 элементов. Я хочу получить только 100 элементов из третьего массива. Я использую следующую функцию JavaScript для синтаксического анализа и получения моих записей.
var fun = function(raw) {JSON.parse(raw).entries};
Выполнение вызова в миллион раз с использованием Nashorn занимает 7.5 ~ 7.8 секунд
(JSObject) invocable.invokeFunction("fun", json);
org.json принимает 20 ~ 21 секунд
new JSONObject(JSON).getJSONArray("entries");
Джексон занимает 6,5 ~ 7 секунд
mapper.readValue(JSON, Entries.class).getEntries();
В этом случае Джексон работает лучше, чем Nashorn, который работает намного лучше, чем org.json. Nashorn API сложнее использовать, чем org.json или Jackson's. В зависимости от ваших требований Джексон и Нашорн могут быть жизнеспособными решениями.
Это тоже смутило меня, потому что я исходил из фона C.
В C переменная - это место в памяти с определенным типом. Назначение переменной копирует данные в ячейку памяти переменной.
Но в Python переменные действуют скорее как указатели на объекты. Поэтому присвоение одной переменной другой не создает копию, она просто превращает это имя переменной в один и тот же объект.
Вы также можете просто создать новый словарь с пониманием словаря. Это позволяет избежать импорта копии.
dout = dict((k,v) for k,v in mydict.items())
Конечно, в python> = 2.7 вы можете сделать:
dout = {k:v for k,v in mydict.items()}
Но для обратной совместимости лучший способ лучше.
Лучший и самый простой способ создать копию dict в обоих Python 2.7 и 3 ...
Чтобы создать копию простого (одноуровневого ) словарь:
1. Используя метод dict (), вместо создания ссылки, которая указывает на существующий dict.
my_dict1 = dict()
my_dict1["message"] = "Hello Python"
print(my_dict1) # {'message':'Hello Python'}
my_dict2 = dict(my_dict1)
print(my_dict2) # {'message':'Hello Python'}
# Made changes in my_dict1
my_dict1["name"] = "Emrit"
print(my_dict1) # {'message':'Hello Python', 'name' : 'Emrit'}
print(my_dict2) # {'message':'Hello Python'}
2. Использование встроенного метода update () для словаря python.
my_dict2 = dict()
my_dict2.update(my_dict1)
print(my_dict2) # {'message':'Hello Python'}
# Made changes in my_dict1
my_dict1["name"] = "Emrit"
print(my_dict1) # {'message':'Hello Python', 'name' : 'Emrit'}
print(my_dict2) # {'message':'Hello Python'}
Чтобы создать копию вложенного или сложного словаря:
Используйте встроенный модуль копирования, который обеспечивает общие операции мелкой и глубокой копии. Этот модуль присутствует как в Python 2.7, так и в 3. *
import copy
my_dict2 = copy.deepcopy(my_dict1)
Вы можете скопировать и отредактировать недавно созданную копию за один проход, вызвав конструктор dict
с дополнительными аргументами ключевого слова:
>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict(dict1, key2="WHY?!")
>>> dict1
{'key2': 'value2', 'key1': 'value1'}
>>> dict2
{'key2': 'WHY?!', 'key1': 'value1'}
Когда вы назначаете dict2 = dict1
, вы не делаете копию dict1
, это приводит к тому, что dict2
является просто другим именем для dict1
.
Чтобы скопировать изменяемые типы, такие как словари , используйте copy
/ deepcopy
модуля copy
.
import copy
dict2 = copy.deepcopy(dict1)
Операторы присваивания в Python не копируют объекты, они создают привязки между объектом и объектом.
, поэтому, dict2 = dict1
, он вызывает другое связывание между dict2
и объектом, к которому dict1
относится.
, если вы хотите скопировать dict, вы можете использовать copy module
. Модуль копирования имеет два интерфейса:
copy.copy(x)
Return a shallow copy of x.
copy.deepcopy(x)
Return a deep copy of x.
Разница между мелким и глубоким копированием применима только для составных объектов (объектов, которые содержат другие объекты, например списки или экземпляры классов):
мелкая копия создает новый составной объект, а затем (по мере возможности) вставляет ссылки в него в объекты, найденные в оригинале.
A глубокая копия создает новый составной объект, а затем рекурсивно вставляет в него копии объектов, найденных в оригинале.
Например, в python 2.7.9:
>>> import copy
>>> a = [1,2,3,4,['a', 'b']]
>>> b = a
>>> c = copy.copy(a)
>>> d = copy.deepcopy(a)
>>> a.append(5)
>>> a[4].append('c')
, а результат:
>>> a
[1, 2, 3, 4, ['a', 'b', 'c'], 5]
>>> b
[1, 2, 3, 4, ['a', 'b', 'c'], 5]
>>> c
[1, 2, 3, 4, ['a', 'b', 'c']]
>>> d
[1, 2, 3, 4, ['a', 'b']]
Python never неявно копирует объекты. Когда вы устанавливаете dict2 = dict1
, вы делаете их ссылкой на один и тот же точный объект dict, поэтому, когда вы его мутируете, все ссылки на него продолжают ссылаться на объект в его текущем состоянии.
Если вы хотите скопируйте dict (что редко), вы должны сделать это явно с помощью
dict2 = dict(dict1)
или
dict2 = dict1.copy()
В python 3.5+ есть более простой способ получить мелкую копию с помощью оператора ** распаковки. Определено Pep 448 .
>>>dict1 = {"key1": "value1", "key2": "value2"}
>>>dict2 = {**dict1}
>>>print(dict2)
{'key1': 'value1', 'key2': 'value2'}
>>>dict2["key2"] = "WHY?!"
>>>print(dict1)
{'key1': 'value1', 'key2': 'value2'}
>>>print(dict2)
{'key1': 'value1', 'key2': 'WHY?!'}
** распаковывает словарь в новый словарь, который затем присваивается dict2.
Мы также можем подтвердить, что каждый словарь имеет отдельный идентификатор.
>>>id(dict1)
178192816
>>>id(dict2)
178192600
Если требуется глубокая копия, то copy.deepcopy () все еще остается в пути.
Как объяснили другие, встроенный dict
не делает то, что вы хотите. Но в Python2 (и, вероятно, 3 тоже) вы можете легко создать класс ValueDict
, который копирует с помощью =
, чтобы вы могли быть уверены, что оригинал не изменится.
class ValueDict(dict):
def __ilshift__(self, args):
result = ValueDict(self)
if isinstance(args, dict):
dict.update(result, args)
else:
dict.__setitem__(result, *args)
return result # Pythonic LVALUE modification
def __irshift__(self, args):
result = ValueDict(self)
dict.__delitem__(result, args)
return result # Pythonic LVALUE modification
def __setitem__(self, k, v):
raise AttributeError, \
"Use \"value_dict<<='%s', ...\" instead of \"d[%s] = ...\"" % (k,k)
def __delitem__(self, k):
raise AttributeError, \
"Use \"value_dict>>='%s'\" instead of \"del d[%s]" % (k,k)
def update(self, d2):
raise AttributeError, \
"Use \"value_dict<<=dict2\" instead of \"value_dict.update(dict2)\""
# test
d = ValueDict()
d <<='apples', 5
d <<='pears', 8
print "d =", d
e = d
e <<='bananas', 1
print "e =", e
print "d =", d
d >>='pears'
print "d =", d
d <<={'blueberries': 2, 'watermelons': 315}
print "d =", d
print "e =", e
print "e['bananas'] =", e['bananas']
# result
d = {'apples': 5, 'pears': 8}
e = {'apples': 5, 'pears': 8, 'bananas': 1}
d = {'apples': 5, 'pears': 8}
d = {'apples': 5}
d = {'watermelons': 315, 'blueberries': 2, 'apples': 5}
e = {'apples': 5, 'pears': 8, 'bananas': 1}
e['bananas'] = 1
# e[0]=3
# would give:
# AttributeError: Use "value_dict<<='0', ..." instead of "d[0] = ..."
Пожалуйста, обратитесь к шаблон изменения lvalue, обсуждаемый здесь: Python 2.7 - чистый синтаксис для модификации lvalue . Главное наблюдение заключается в том, что str
и int
ведут себя как значения в Python (даже если они фактически являются неизменяемыми объектами под капотом). В то время как вы это замечаете, также обратите внимание, что ничего особенного в str
или int
нет. dict
можно использовать почти одинаково, и я могу думать о многих случаях, когда ValueDict
имеет смысл.
dict1
- это символ, который ссылается на базовый объект словаря. Назначение dict1
- dict2
просто присваивает ту же ссылку. Изменение значения ключа с помощью символа dict2
изменяет базовый объект, что также влияет на dict1
. Это запутанно.
Гораздо проще рассуждать о неизменяемых значениях, чем ссылки, поэтому по возможности делайте копии:
person = {'name': 'Mary', 'age': 25}
one_year_later = {**person, 'age': 26} # does not mutate person dict
Это синтаксически то же самое, что:
one_year_later = dict(person, age=26)
Поскольку python работает со ссылкой, поэтому, когда вы сделали dict2 = dict1, вы передаете ссылку на dict2, это было то же самое, что и dict1. Итак, когда вы вносите изменения в dict1 или dict2, вы меняете ссылку, и оба определяют chages. Извините, если я что-то ошибся на английском.
dict2 = dict1
не копирует словарь. Это просто дает программисту второй способ (dict2
) ссылаться на тот же словарь.
Вы можете использовать напрямую:
dict2 = eval(repr(dict1))
, где объект dict2 является независимой копией dict1, поэтому вы можете изменить dict2 без влияния на dict1.
Это работает для любого типа объект. [/ д2]
, потому что, dict2 = dict1, dict2 содержит ссылку на dict1. Оба dict1 и dict2 указывают на то же место в памяти. Это обычный случай при работе с изменяемыми объектами в python. Когда вы работаете с изменяемыми объектами в python, вы должны быть осторожны, поскольку его трудно отлаживать. Например, в следующем примере.
my_users = {
'ids':[1,2],
'blocked_ids':[5,6,7]
}
ids = my_users.get('ids')
ids.extend(my_users.get('blocked_ids')) #all_ids
print ids#output:[1, 2, 5, 6, 7]
print my_users #output:{'blocked_ids': [5, 6, 7], 'ids': [1, 2, 5, 6, 7]}
Это примерное намерение состоит в том, чтобы получить все идентификаторы пользователей, включая заблокированные идентификаторы. То, что мы получили от переменной ids, но также непреднамеренно обновили значение my_users . когда вы расширили ids с помощью заблокированных_id , мои пользователи обновились, потому что ids относятся к my_users .