Это сработало для меня:
Я пытаюсь приблизиться к размеру шрифта на основе ширины / высоты от установки размера шрифта: 10 пикселей. В основном идея: «Если у меня ширина 20px и высота 11px с размером шрифта: 10px, то каков будет максимальный размер шрифта для математического контейнера с шириной 50px и высотой 30px?»
ответ - система с двойной пропорцией:
{20: 10 = 50: X, 11: 10 = 30: Y} = {X = (10 * 50) / 20, Y = (10 * 30) / 11}
Теперь X - размер шрифта, который будет соответствовать ширине, Y - размер шрифта, который будет соответствовать высоте; возьмите наименьшее значение
function getMaxFontSizeApprox(el){
var fontSize = 10;
var p = el.parentNode;
var parent_h = p.offsetHeight ? p.offsetHeight : p.style.pixelHeight;
if(!parent_h)parent_h = 0;
var parent_w = p.offsetHeight ? p.offsetWidth : p.style.pixelWidth;
if(!parent_w)parent_w = 0;
el.style.fontSize = fontSize + "px";
var el_h = el.offsetHeight ? el.offsetHeight : el.style.pixelHeight;
if(!el_h)el_h = 0;
var el_w = el.offsetHeight ? el.offsetWidth : el.style.pixelWidth;
if(!el_w)el_w = 0;
//0.5 is the error on the measure that js does
//if real measure had been 12.49 px => js would have said 12px
//so we think about the worst case when could have, we add 0.5 to
//compensate the round error
var fs1 = (fontSize*(parent_w + 0.5))/(el_w + 0.5);
var fs2 = (fontSize*(parent_h) + 0.5)/(el_h + 0.5);
fontSize = Math.floor(Math.min(fs1,fs2));
el.style.fontSize = fontSize + "px";
return fontSize;
}
NB: аргумент функции должен быть элементом span или элементом, который меньше, чем родительский, в противном случае, если дети и родитель имеют одинаковые функции ширины и высоты, неудачу
Взгляните на это:
>>> a = 256
>>> b = 256
>>> id(a)
9987148
>>> id(b)
9987148
>>> a = 257
>>> b = 257
>>> id(a)
11662816
>>> id(b)
11662828
EDIT: Вот что я нашел в документации Python 2, «Plain Integer Objects» (То же самое для Python 3 ):
Текущая реализация хранит массив целых объектов для всех целых чисел от -5 до 256, когда вы создаете int в этом диапазоне, на котором вы просто возвращаетесь ссылку на существующий объект. Поэтому должно быть возможно изменить значение 1. Я подозреваю, что поведение Python в этом случае не определено. : -)
Оператор «is» Python ведет себя неожиданно с целыми числами?
Вкратце - позвольте мне подчеркнуть: Не используйте
is
для сравнения целых чисел.Это не поведение, о котором вы должны ожидать.
Вместо этого используйте
==
и!=
для сравнения для равенства и неравенства соответственно. Например:>>> a = 1000 >>> a == 1000 # Test integers like this, True >>> a != 5000 # or this! True >>> a is 1000 # Don't do this! - Don't use `is` to test integers!! False
Объяснение
Чтобы это знать, вам нужно знать следующее.
Во-первых, что делает
is
? Это оператор сравнения. Из документации :Операторы
is
иis not
проверяют идентификатор объекта:x is y
истинно тогда и только тогда, когда x и y являются одинаковыми объект.x is not y
дает обратное значение истины.И поэтому следующие эквиваленты.
>>> a is b >>> id(a) == id(b)
id
Вернуть «идентификатор» объекта. Это целое число (или длинное целое число), которое гарантировано будет уникальным и постоянным для этого объекта в течение его жизни. Два объекта с неперекрывающимся временем жизни могут иметь одинаковое значениеid()
.Обратите внимание, что тот факт, что идентификатор объекта в CPython (эталонная реализация Python) - это местоположение в память - это деталь реализации. Другие реализации Python (например, Jython или IronPython) могут легко иметь другую реализацию для
id
.Итак, каков прецедент для
is
? PEP8 описывает :. Сравнение с синглонами типа
< / blockquote>None
всегда должно выполняться с помощьюis
илиis not
, никогда не выполняемых операторов равенства.Вопрос
Вы задаете и задаете следующий вопрос (с кодом):
Почему в Python происходит непредвиденное поведение?
>>> a = 256 >>> b = 256 >>> a is b True # This is an expected result
Ожидаемый результат not . Почему это ожидалось? Это означает, что целые числа, оцененные в
256
, на которые ссылаются какa
, так иb
, являются одним и тем же экземпляром целого числа. Целые числа неизменны в Python, поэтому они не могут измениться. Это не должно влиять на какой-либо код. Этого нельзя ожидать. Это всего лишь деталь реализации.Но, возможно, мы должны быть рады, что каждый отдельный экземпляр в памяти не будет каждый раз, когда мы укажем, что значение равно 256.
>>> a = 257 >>> b = 257 >>> a is b False # What happened here? Why is this False?
Looks например, теперь у нас есть два отдельных экземпляра целых чисел со значением
257
в памяти. Поскольку целые числа неизменны, это отнимает память. Будем надеяться, что мы не будем тратить много денег. Наверное, нет. Но это поведение не гарантируется.>>> 257 is 257 True # Yet the literal numbers compare properly
Ну, похоже, что ваша конкретная реализация Python пытается быть умной и не создавать избыточно ценные целые числа в памяти, если только она не имеет к. Вы, кажется, указываете, что используете референтную реализацию Python, которая является CPython. Хорошо для CPython.
Возможно, было бы лучше, если бы CPython мог сделать это глобально, если бы он мог сделать это дешево (так как это будет стоить в поиске), возможно, другая реализация.
Но что касается влияния на код, вам все равно, является ли целое число конкретным экземпляром целого числа. Вы должны знать, что такое значение этого экземпляра, и вы использовали бы для этого обычные операторы сравнения, т. Е.
==
.Что
is
делает
is
проверяет, чтоid
двух объектов одинаковы. В CPythonid
- это место в памяти, но это может быть какое-то другое уникально идентифицирующее число в другой реализации. Чтобы переформулировать это с помощью кода:>>> a is b
совпадает с
>>> id(a) == id(b)
Почему мы хотели бы использовать
is
тогда?Это может быть очень быстрой проверкой, чтобы сказать, проверяя, являются ли две очень длинные строки равными по стоимости. Но поскольку это относится к уникальности объекта, мы, таким образом, имеем ограниченные прецеденты. Фактически, мы в основном хотим использовать его для проверки на
None
, который является одноэлементным (единственный экземпляр, существующий в одном месте в памяти). Мы могли бы создать другие синглтоны, если есть потенциал для их объединения, что мы можем проверить с помощьюis
, но они относительно редки. Вот пример (будет работать в Python 2 и 3), напримерSENTINEL_SINGLETON = object() # this will only be created one time. def foo(keyword_argument=None): if keyword_argument is None: print('no argument given to foo') bar() bar(keyword_argument) bar('baz') def bar(keyword_argument=SENTINEL_SINGLETON): # SENTINEL_SINGLETON tells us if we were not passed anything # as None is a legitimate potential argument we could get. if keyword_argument is SENTINEL_SINGLETON: print('no argument given to bar') else: print('argument to bar: {0}'.format(keyword_argument)) foo()
Что печатает:
no argument given to foo no argument given to bar argument to bar: None argument to bar: baz
Итак, мы видим, что с
is
и дозорным, мы могут различать, когдаbar
вызывается без аргументов и когда он вызывается с помощьюNone
. Это первичные варианты использования дляis
- do not использовать его для проверки равенства целых чисел, строк, кортежей или других подобных вещей.
Есть еще одна проблема, которая не указана ни в одном из существующих ответов. Python разрешено объединять любые два неизменных значения, и предварительно созданные значения малых значений не являются единственным способом, которым это может случиться. Реализация Python никогда не гарантирована , но все они делают это не более, чем просто малые int.
Во-первых, есть еще некоторые предварительно созданные такие как пустые tuple
, str
и bytes
и некоторые короткие строки (в CPython 3.6 это 256 односимвольных строк Latin-1). Например:
>>> a = ()
>>> b = ()
>>> a is b
True
Но также даже не созданные заранее значения могут быть одинаковыми. Рассмотрим следующие примеры:
>>> c = 257
>>> d = 257
>>> c is d
False
>>> e, f = 258, 258
>>> e is f
True
И это не ограничено значениями int
:
>>> g, h = 42.23e100, 42.23e100
>>> g is h
True
Очевидно, что CPython не поставляется с предварительно созданным float
для параметра 42.23e100
. Итак, что здесь происходит?
Компилятор CPython будет объединять постоянные значения некоторых известных неизменяемых типов, таких как int
, float
, str
, bytes
, в одном модуле компиляции. Для модуля весь модуль является единицей компиляции, но в интерактивном интерпретаторе каждый оператор представляет собой отдельный блок компиляции. Поскольку c
и d
определены в отдельных утверждениях, их значения не объединяются. Поскольку e
и f
определены в том же самом заявлении, их значения сливаются.
Вы можете видеть, что происходит, разобрав байт-код. Попробуйте определить функцию, которая выполняет e, f = 128, 128
, а затем называет dis.dis
на ней, и вы увидите, что существует одно постоянное значение (128, 128)
>>> def f(): i, j = 258, 258
>>> dis.dis(f)
1 0 LOAD_CONST 2 ((128, 128))
2 UNPACK_SEQUENCE 2
4 STORE_FAST 0 (i)
6 STORE_FAST 1 (j)
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
>>> f.__code__.co_consts
(None, 128, (128, 128))
>>> id(f.__code__.co_consts[1], f.__code__.co_consts[2][0], f.__code__.co_consts[2][1])
4305296480, 4305296480, 4305296480
. Вы можете заметить, что компилятор сохранил 128
как константу, даже если он фактически не используется байтовым кодом, что дает вам представление о том, как мало оптимизирует компилятор CPython. Это означает, что (непустые) кортежи на самом деле не сливаются:
>>> k, l = (1, 2), (1, 2)
>>> k is l
False
Поместите это в функцию, dis
, и посмотрите на co_consts
- есть 1
и 2
, два (1, 2)
кортежа, которые имеют одинаковые 1
и 2
, но не идентичны, и кортеж ((1, 2), (1, 2))
, который имеет два разных одинаковых кортежа.
Есть еще одна оптимизация, которую выполняет CPython: string interning. В отличие от сгибания константы компилятора, это не ограничивается литералами исходного кода:
>>> m = 'abc'
>>> n = 'abc'
>>> m is n
True
С другой стороны, он ограничен типом str
и строками типа внутреннего хранилища «ascii compact», «compact» или «legacy ready» , и во многих случаях только «ascii compact» будет интернирован.
Во всяком случае, правила для чего значения должны быть, могут быть или не могут отличаться от реализации до реализации, а также между версиями одной и той же реализации и, возможно, даже между прогонами одного и того же кода в одной и той же копии одной и той же реализации.
Возможно, стоит изучить правила для одного конкретного Python для удовольствия. Но не стоит полагаться на них в вашем коде. Единственное безопасное правило:
Или, другими словами, использовать is
только для проверки документированных синглетов (например, None
) или которые создаются только в одном месте в код (например, идиома _sentinel = object()
).
Для объектов неизменяемых значений, таких как int, строки или даты, идентификация объекта не особенно полезна. Лучше думать о равенстве. Идентификация - это, по сути, деталь реализации объектов значения - поскольку они неизменяемы, нет эффективной разницы между наличием нескольких ссылок на один и тот же объект или несколько объектов.
Я думаю, что ваши гипотезы верны. Эксперимент с id
(идентификация объекта):
In [1]: id(255)
Out[1]: 146349024
In [2]: id(255)
Out[2]: 146349024
In [3]: id(257)
Out[3]: 146802752
In [4]: id(257)
Out[4]: 148993740
In [5]: a=255
In [6]: b=255
In [7]: c=257
In [8]: d=257
In [9]: id(a), id(b), id(c), id(d)
Out[9]: (146349024, 146349024, 146783024, 146804020)
Похоже, что числа <= 255
рассматриваются как литералы, а что-то выше, обрабатывается по-разному!
Это также происходит со строками:
>>> s = b = 'somestr'
>>> s == b, s is b, id(s), id(b)
(True, True, 4555519392, 4555519392)
Теперь все кажется прекрасным.
>>> s = 'somestr'
>>> b = 'somestr'
>>> s == b, s is b, id(s), id(b)
(True, True, 4555519392, 4555519392)
Это тоже ожидается.
>>> s1 = b1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> s1 == b1, s1 is b1, id(s1), id(b1)
(True, True, 4555308080, 4555308080)
>>> s1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> b1 = 'somestrdaasd ad ad asd as dasddsg,dlfg ,;dflg, dfg a'
>>> s1 == b1, s1 is b1, id(s1), id(b1)
(True, False, 4555308176, 4555308272)
Теперь это неожиданным.
'xx'
соответствует ожидаемой, как и 'xxx'
, но 'x x'
- нет.
– Brian
16 December 2015 в 21:48
xx
, эта строка уже интернирована; и может быть эвристика, которая делает это, если она просто напоминает имя. Как и в случае с цифрами, это можно сделать, потому что они неизменны. docs.python.org/2/library/functions.html#intern guilload.com/python-string-interning
– Yann Vernier
22 September 2016 в 07:48
Посмотрите здесь
Текущая реализация сохраняет массив целых объектов для всех целых чисел от -5 до 256, когда вы создаете int в этом диапазоне вы фактически просто возвращаете ссылку на существующий объект.
Как вы можете проверить исходный файл intobject.c , Python кэширует мелкие целые числа для эффективности. Каждый раз, когда вы создаете ссылку на небольшое целое число, вы ссылаетесь на кэшированное маленькое целое число, а не на новый объект. 257 не является маленьким целым числом, поэтому он вычисляется как другой объект.
Для этого лучше использовать ==
.
is
является оператором равенства идентичности (функционирующим как id(a) == id(b)
); просто два одинаковых числа не обязательно являются одним и тем же объектом. По соображениям производительности некоторые маленькие целые числа являются memoized , поэтому они будут иметь тенденцию быть одинаковыми (это можно сделать, поскольку они являются неизменяемыми).
PHP ===
, с другой стороны, описывается как проверка равенства и типа: x == y and type(x) == type(y)
в соответствии с комментарием Пауло Фрейтаса. Этого достаточно для общих чисел, но отличается от is
для классов, которые определяют __eq__
абсурдным образом:
class Unequal:
def __eq__(self, other):
return False
PHP, по-видимому, допускает то же самое для «встроенных» классов (что Я подразумеваю реализацию на уровне C, а не в PHP). Немного менее абсурдным использованием может быть объект таймера, который имеет различное значение каждый раз, когда он используется как число. Весьма почему вы хотели бы эмулировать Visual Basic Now
, а не показывать, что это оценка с time.time()
, которую я не знаю.
Грег Хьюджилл (OP) сделал один пояснительный комментарий «Моя цель это сравнение идентичности объекта, а не равенства стоимости. За исключением чисел, где я хочу рассматривать идентичность объекта так же, как равенство стоимости. "
У этого был бы еще один ответ, поскольку мы должны классифицировать вещи как числа или нет, чтобы выбрать, сравнивать ли мы с ==
или is
. CPython определяет протокол number , включая PyNumber_Check, но это не доступно из самого Python.
Мы могли бы попытаться использовать isinstance
со всеми которые мы знаем, но это неизбежно было бы неполным. Модуль типов содержит список StringTypes, но не NumberTypes. Начиная с Python 2.6, встроенные числовые классы имеют базовый класс numbers.Number
, но он имеет ту же проблему:
import numpy, numbers
assert not issubclass(numpy.int16,numbers.Number)
assert issubclass(int,numbers.Number)
Кстати, NumPy будет производить отдельные экземпляры с низкими номерами.
На самом деле я не знаю ответа на этот вариант вопроса. Я предполагаю, что теоретически можно использовать ctypes для вызова PyNumber_Check
, но даже эту функцию обсуждали , и это, конечно, не переносимо. Мы просто должны быть менее конкретными в отношении того, что мы тестируем сейчас.
В конце концов, эта проблема связана с тем, что Python первоначально не имел дерева типов с предикатами, такими как Scheme number?
или класса Haskell Num . is
проверяет идентификацию объекта, а не равенство значения. PHP также имеет красочную историю, где ===
, по-видимому, ведет себя как is
только на объектах в PHP5, но не на PHP4 . Таковы растущие трудности перемещения по языкам (включая версии одного).