Сравнение номера версии в Python

Как виртуальные функции реализованы на глубоком уровне?

От "Виртуальные функции в C++" :

Каждый раз, когда программе объявили виртуальную функцию, v - таблица создается для класса. V-таблица состоит из адресов к виртуальным функциям для классов, которые содержат одну или несколько виртуальных функций. Объект класса, содержащего виртуальную функцию, содержит виртуальный указатель, который указывает на базовый адрес виртуальной таблицы в памяти. Каждый раз, когда существует вызов виртуальной функции, v-таблица используется для разрешения к функциональному адресу. Объект класса, который содержит одну или несколько виртуальных функций, содержит виртуальный указатель, названный vptr в самом начале объекта в памяти. Следовательно размер объекта в этом случае увеличивается размером указателя. Этот vptr содержит базовый адрес виртуальной таблицы в памяти. Обратите внимание, что виртуальные таблицы являются конкретным классом, т.е. существует только одна виртуальная таблица для класса независимо от количества виртуальных функций, которые это содержит. Эта виртуальная таблица в свою очередь содержит базовые адреса одной или нескольких виртуальных функций класса. В то время, когда виртуальная функция вызвана на объекте, vptr того объекта обеспечивает базовый адрес виртуальной таблицы для того класса в памяти. Эта таблица используется для разрешения вызова функции, поскольку это содержит адреса всех виртуальных функций того класса. Это - то, как динамическое связывание разрешено во время вызова виртуальной функции.

vtable может быть изменено или даже непосредственно получено доступ во времени выполнения?

Универсально, я полагаю, что ответ является "нет". Вы могли сделать некоторое искажение памяти для нахождения vtable, но Вы все еще не будете знать то, на что функциональная подпись похожа для вызова его. Что-либо, чего Вы хотели бы достигнуть с этой способностью (который поддерживает язык) должно быть возможным без доступа к vtable непосредственно или изменению его во времени выполнения. Также отметьте, спецификация языка C++ не делает , определяют, что vtables требуются - однако, именно так большинство компиляторов реализует виртуальные функции.

vtable существует для всех объектов, или только тех, которые имеют по крайней мере одну виртуальную функцию?

я верю , ответ здесь, "он зависит от реализации", так как спецификация не требует vtables во-первых. Однако на практике я полагаю, что все современные компиляторы только создают vtable, если класс имеет по крайней мере 1 виртуальную функцию. Существует пространство, наверху связанное с vtable и время, наверху связанное с вызыванием виртуальной функции по сравнению с невиртуальной функцией.

абстрактные классы просто имеют ПУСТОЙ УКАЗАТЕЛЬ для указателя функции по крайней мере одной записи?

ответ - он, является неуказанным спецификацией языка, таким образом, это зависит от реализации. Вызов чистой виртуальной функции приводит к неопределенному поведению, если это не определяется (который это обычно не) (ISO/IEC 14882:2003 10.4-2). На практике это действительно выделяет слот в vtable для функции, но не присваивает адрес ему. Это оставляет vtable неполное, которое требует, чтобы производные классы реализовали функцию и завершили vtable. Некоторые реализации действительно просто помещают Нулевого указателя в vtable запись; другие реализации помещают указатель на фиктивный метод, который делает что-то подобное утверждению.

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

Делает наличие единственной виртуальной функции, замедляют целый класс или только вызов к функции, которая является виртуальной?

Это добирается до края моего знания, таким образом, кто-то, помогите мне здесь, если я неправ!

я полагаю , что только функции, которые являются виртуальными в классе, испытывают хит производительности времени, связанный с вызыванием виртуальной функции по сравнению с невиртуальной функцией. Пространство наверху для класса там так или иначе. Обратите внимание, что, если существует vtable, существует только 1 на класс , не один на [1 110] объект .

скорость становится затронутой, если виртуальная функция на самом деле переопределяется или нет, или это не имеет никакого эффекта, пока это является виртуальным?

я не верю времени выполнения виртуальной функции, которая переопределяется уменьшения по сравнению с вызыванием основной виртуальной функции. Однако существует дополнительное пространство наверху для класса, связанного с определением другого vtable для производного класса по сравнению с базовым классом.

Дополнительные Ресурсы:

http://www.codersource.net/published/view/325/virtual_functions_in.aspx (через путь назад машина)
http://en.wikipedia.org/wiki/Virtual_table
http://www.codesourcery.com/public/cxx-abi/abi.html#vtable

95
задан Stevoisiak supports Monica 28 February 2018 в 16:54
поделиться

8 ответов

Удалите неинтересную часть строки (завершающие нули и точки), а затем сравните списки чисел.

import re

def mycmp(version1, version2):
    def normalize(v):
        return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")]
    return cmp(normalize(version1), normalize(version2))

Это тот же подход, что и Pär Wieslander, но немного более компактный:

Вот несколько тестов, благодаря « Как сравнить две строки в формате версий, разделенных точками в Bash? »:

assert mycmp("1", "1") == 0
assert mycmp("2.1", "2.2") < 0
assert mycmp("3.0.4.10", "3.0.4.2") > 0
assert mycmp("4.08", "4.08.01") < 0
assert mycmp("3.2.1.9.8144", "3.2") > 0
assert mycmp("3.2", "3.2.1.9.8144") < 0
assert mycmp("1.2", "2.1") < 0
assert mycmp("2.1", "1.2") > 0
assert mycmp("5.6.7", "5.6.7") == 0
assert mycmp("1.01.1", "1.1.1") == 0
assert mycmp("1.1.1", "1.01.1") == 0
assert mycmp("1", "1.0") == 0
assert mycmp("1.0", "1") == 0
assert mycmp("1.0", "1.0.1") < 0
assert mycmp("1.0.1", "1.0") > 0
assert mycmp("1.0.2.0", "1.0.2") == 0
36
ответ дан 24 November 2019 в 05:42
поделиться

Мое предпочтительное решение:

Добавление в строку дополнительных нулей и использование только четырех первых легко понять, не требует никакого регулярного выражения, а лямбда более или менее читаема. Я использую две строки для удобства чтения, для меня элегантность коротка и проста.

def mycmp(version1,version2):
  tup = lambda x: [int(y) for y in (x+'.0.0.0.0').split('.')][:4]
  return cmp(tup(version1),tup(version2))
-1
ответ дан 24 November 2019 в 05:42
поделиться

Самое трудное для чтения решение, но тем не менее однострочное! и использование итераторов для быстрой работы.

next((c for c in imap(lambda x,y:cmp(int(x or 0),int(y or 0)),
            v1.split('.'),v2.split('.')) if c), 0)

то есть для Python2.6 и 3. + кстати, Python 2.5 и старше должны улавливать StopIteration.

1
ответ дан 24 November 2019 в 05:42
поделиться
def compare_version(v1, v2):
    return cmp(*tuple(zip(*map(lambda x, y: (x or 0, y or 0), 
           [int(x) for x in v1.split('.')], [int(y) for y in v2.split('.')]))))

Это однострочный (разделен для удобства чтения). Не уверен в удобочитаемости ...

2
ответ дан 24 November 2019 в 05:42
поделиться

Удалить завершающие .0 и .00 с помощью регулярного выражения, разделить и использовать функцию cmp , которая правильно сравнивает массивы:

def mycmp(v1,v2):
 c1=map(int,re.sub('(\.0+)+\Z','',v1).split('.'))
 c2=map(int,re.sub('(\.0+)+\Z','',v2).split('.'))
 return cmp(c1,c2)

И, конечно, вы можете преобразовать его в однострочное, если не возражаете против длинных строк.

6
ответ дан 24 November 2019 в 05:42
поделиться

Это немного более компактно, чем ваше предложение. Вместо того, чтобы заполнять более короткую версию нулями, я удаляю конечные нули из списков версий после разделения.

def normalize_version(v):
    parts = [int(x) for x in v.split(".")]
    while parts[-1] == 0:
        parts.pop()
    return parts

def mycmp(v1, v2):
    return cmp(normalize_version(v1), normalize_version(v2))
10
ответ дан 24 November 2019 в 05:42
поделиться

Нет необходимости перебирать кортежи версий. Встроенный оператор сравнения списков и кортежей уже работает точно так, как вы этого хотите. Вам просто нужно расширить списки версий до соответствующей длины. В python 2.6 вы можете использовать izip_longest для заполнения последовательностей.

from itertools import izip_longest
def version_cmp(v1, v2):
    parts1, parts2 = [map(int, v.split('.')) for v in [v1, v2]]
    parts1, parts2 = zip(*izip_longest(parts1, parts2, fillvalue=0))
    return cmp(parts1, parts2)

В более ранних версиях требуется взлом карты.

def version_cmp(v1, v2):
    parts1, parts2 = [map(int, v.split('.')) for v in [v1, v2]]
    parts1, parts2 = zip(*map(lambda p1,p2: (p1 or 0, p2 or 0), parts1, parts2))
    return cmp(parts1, parts2)
12
ответ дан 24 November 2019 в 05:42
поделиться

Считается ли повторное использование элегантностью в данном случае? :)

# pkg_resources is in setuptools
# See http://peak.telecommunity.com/DevCenter/PkgResources#parsing-utilities
def mycmp(a, b):
    from pkg_resources import parse_version as V
    return cmp(V(a),V(b))
30
ответ дан 24 November 2019 в 05:42
поделиться
Другие вопросы по тегам:

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