Все это происходит из тома 2 Руководства разработчика программного обеспечения Intel 64 и IA-32 .
FCOMI
устанавливает только некоторые из флагов, которые CMP
делает , Ваш код имеет %st(0) == 9
и %st(1) == 10
. (Так как это стек, на который они загружены), ссылаясь на таблицу на стр. 3-348 в томе 2А, вы можете видеть, что это так «ST0 & lt; ST (i)», поэтому он очистит ZF и PF и набор CF. Между тем на стр. 3-544 Vol. 2A вы можете прочитать, что JG
означает «Переход короткий, если больше (ZF = 0 и SF = OF)». Другими словами, он тестирует значки, переполнения и нулевые флаги, но FCOMI
не устанавливает знак или переполнение!
В зависимости от условий, которые вы хотите перескакивать, вы должны посмотреть на возможные результаты сравнения и решите, когда вы хотите прыгать.
+--------------------+---+---+---+ | Comparison results | Z | P | C | +--------------------+---+---+---+ | ST0 > ST(i) | 0 | 0 | 0 | | ST0 < ST(i) | 0 | 0 | 1 | | ST0 = ST(i) | 1 | 0 | 0 | +--------------------+---+---+---+
Я сделал эту небольшую таблицу, чтобы было легче выяснить:
+--------------+---+---+-----+------------------------------------+ | Test | Z | C | Jcc | Notes | +--------------+---+---+-----+------------------------------------+ | ST0 < ST(i) | X | 1 | JB | ZF will never be set when CF = 1 | | ST0 <= ST(i) | 1 | 1 | JBE | Either ZF or CF is ok | | ST0 == ST(i) | 1 | X | JE | CF will never be set in this case | | ST0 != ST(i) | 0 | X | JNE | | | ST0 >= ST(i) | X | 0 | JAE | As long as CF is clear we are good | | ST0 > ST(i) | 0 | 0 | JA | Both CF and ZF must be clear | +--------------+---+---+-----+------------------------------------+ Legend: X: don't care, 0: clear, 1: set
Иными словами, коды условий соответствуют тем для использования неподписанных сравнений. То же самое происходит, если вы используете FMOVcc
.
Если операнд (или оба) с fcomi
равен NaN, он устанавливает ZF=1 PF=1 CF=1
. (FP-сравнения имеют 4 возможных результата: >
, <
, ==
или неупорядоченные). Если вам небезразличен ваш код с помощью NaN, вам может потребоваться дополнительная jp
или jnp
. Но не всегда: например, ja
истинно, только если CF = 0 и ZF = 0, поэтому он не будет принят в неупорядоченном случае. Если вы хотите, чтобы неупорядоченный случай выполнял тот же путь выполнения, что и ниже или равный, тогда вам нужно ja
.
Здесь вы должны использовать JA
, если хотите, чтобы он печатался ( т.е. if (!(f2 > f1)) { puts("hello"); }
) и JBE
, если вы этого не сделаете (соответствует if (!(f2 <= f1)) { puts("hello"); }
). (Обратите внимание, что это может быть немного запутанным из-за того, что мы печатаем только, если мы не прыгаем).
Что касается вашего второго вопроса: по умолчанию fcomi
ничего не всплывает. Вы хотите, чтобы его близкий родственник fcomip
появился %st0
. Вы всегда должны очищать стек регистра fpu после использования, поэтому вся ваша программа заканчивается так, будто вы хотите, чтобы сообщение было напечатано:
.section .rodata
msg: .ascii "Hallo\n\0"
f1: .float 10.0
f2: .float 9.0
.globl main
.type main, @function
main:
flds f1
flds f2
fcomip
fstp %st(0) # to clear stack
ja leb # won't jump, jbe will
pushl $msg
call printf
addl $4, %esp
leb:
pushl $0
call exit
Вы хотите это:
class a:
def __init__(self):
self.list = []
Объявление переменных внутри объявления класса делает их членами класса, а не членами экземпляра. Объявление их внутри метода __init__
гарантирует, что новый экземпляр членов создается рядом с каждым новым экземпляром объекта, что является поведением, которое вы ищете.
Я думаю, что предоставленные ответы вводят в заблуждение. Свойство, определенное внутри класса, становится свойством экземпляра, когда объект создается, независимо от того, как вы его определяете. Таким образом, копии a.list
сделаны, а x.list
и y.list
- разные копии. Причина, по которой они кажутся одинаковыми, заключается в том, что они оба являются псевдонимами в том же списке. Но это следствие того, как работают списки, а не того, как работают классы. Если бы вы делали то же самое с числами вместо списков (или просто используя + = вместо append, что создало бы новый список), вы увидите, что изменение x.attr
не влияет на изменение y.attr
.
Определяет self.list
внутри __init__
, потому что функция вызывается дважды, один раз для каждого экземпляра объекта, и поэтому создается два разных списка.
x.list
и y.list
просматриваются сначала в экземпляре, а затем, когда их не найти, обратитесь к базовому объекту класса. Таким образом, они являются той же ссылкой i>, атрибутом класса. Это not i> несколько ссылок на один и тот же список.
– Martijn Pieters♦
11 March 2013 в 12:20
Вы объявили «список» как «свойство уровня класса», а не «свойство уровня экземпляра». Чтобы иметь свойства, определенные на уровне экземпляра, вам необходимо инициализировать их путем ссылки с параметром «self» в методе __init__
(или в другом месте в зависимости от ситуации).
Вы не строго нужно инициализировать свойства экземпляра в методе __init__
, но это упрощает понимание.
Принятый ответ работает, но немного больше объяснений не повредит.
Атрибуты класса не становятся атрибутами экземпляра при создании экземпляра. Они становятся атрибутами экземпляра, когда им присваивается значение.
В исходном коде значение no присваивается атрибуту list
после создания экземпляра; поэтому он остается атрибутом класса. Определение списка внутри __init__
работает, потому что __init__
вызывается после создания экземпляра. В качестве альтернативы, этот код также выдаст желаемый результат:
>>> class a:
list = []
>>> y = a()
>>> x = a()
>>> x.list = []
>>> y.list = []
>>> x.list.append(1)
>>> y.list.append(2)
>>> x.list.append(3)
>>> y.list.append(4)
>>> print(x.list)
[1, 3]
>>> print(y.list)
[2, 4]
Однако запутанный сценарий в вопросе никогда не случится с неизменяемыми объектами, такими как числа и строки, поскольку их значение не может быть изменено без назначения. Например, код, аналогичный оригиналу со строковым атрибутом, работает без каких-либо проблем:
>>> class a:
string = ''
>>> x = a()
>>> y = a()
>>> x.string += 'x'
>>> y.string += 'y'
>>> x.string
'x'
>>> y.string
'y'
Итак, чтобы суммировать: атрибуты класса становятся атрибутами экземпляра тогда и только тогда, когда им присваивается значение после создания, в методе __init__
или нет. Это хорошо, потому что таким образом вы можете иметь статические атрибуты, если никогда не присваиваете значение атрибуту после создания экземпляра.
Да, вы должны объявить в «конструкторе», если хотите, чтобы список стал свойством объекта, а не свойством класса.
Чтобы защитить вашу переменную, совместно используемую другим экземпляром, вам нужно создать новую переменную экземпляра каждый раз, когда вы создаете экземпляр. Когда вы объявляете переменную внутри класса, она является переменной класса и совместно используется всем экземпляром. Если вы хотите сделать это, например, необходимо использовать метод init для повторной инициализации переменной, ссылаясь на функцию экземпляра
init (). Эта специальная функция вызывается всякий раз, когда создается новый объект этого класса.
Этот тип функции также называется конструкторами в объектно-ориентированном программировании (ООП). Обычно мы используем его для инициализации всех переменных.
blockquote>Например:
class example: list=[] #This is class variable shared by all instance def __init__(self): self.list = [] #This is instance variable referred to specific instance
Несмотря на то, что принятый anwer присутствует, я хотел бы добавить немного описания.
Давайте сделаем небольшое упражнение
, прежде всего определим класс следующим образом:
class A:
temp='Skyharbor'
def __init__(self, x):
self.x=x
def change(self, y):
self.temp=y
Итак, что мы имеем здесь?
temp
, который является строкой self.x
Довольно прямо вперед до сих пор? Теперь давайте начнем играть с этим классом. Давайте сначала инициализируем этот класс:
a = A('Tesseract')
Теперь сделайте следующее:
>>> print a.temp
Skyharbor
>>> print A.temp
Skyharbor
Ну, a.temp
работал так, как ожидалось, но как, черт возьми, A.temp
работал? Ну, это сработало, потому что temp - это атрибут класса. Все в python - это объект. Здесь A также является объектом класса type
. Таким образом, temp атрибута является атрибутом, принадлежащим классу A, и если вы измените значение temp через A (а не через экземпляр a), измененное значение будет отражено во всем экземпляре класса A. Давайте продолжим и сделаем это:
>>> A.temp = 'Monuments'
>>> print A.temp
Monuments
>>> print a.temp
Monuments
Интересно, не так ли? И обратите внимание, что id (a.temp) и id (A.temp) все те же
Любой объект Python автоматически получает атрибут dict, который содержит его список атрибутов. Давайте рассмотрим, что этот словарь содержит для наших объектов-объектов:
>>> print A.__dict__
{
'change': <function change at 0x7f5e26fee6e0>,
'__module__': '__main__',
'__init__': <function __init__ at 0x7f5e26fee668>,
'temp': 'Monuments',
'__doc__': None
}
>>> print a.__dict__
{x: 'Tesseract'}
Обратите внимание, что атрибут temp
указан среди атрибутов класса A, тогда как x указан для экземпляра
Итак, что мы получаем определенное значение a.temp
, если оно даже не указано для экземпляра a. Ну, это волшебство метода __getattribute__()
. В Python точечный синтаксис автоматически вызывает этот метод, поэтому, когда мы пишем a.temp
, Python выполняет a.getattribute ('temp'). Этот метод выполняет действие поиска атрибута, то есть находит значение атрибута, просматривая в разных местах.
Стандартная реализация __getattribute__()
сначала ищет внутренний словарь (dict) объекта, а затем тип самого объекта. В этом случае a.__getattribute__('temp')
выполняет сначала a.__dict__['temp']
, а затем a.__class__.__dict__['temp']
Теперь давайте воспользуемся нашим методом change
:
>>> a.change('Intervals')
>>> print a.temp
Intervals
>>> print A.temp
Monuments
Ну, теперь, когда мы использовали себя , print a.temp
дает другое значение из print A.temp
.
Теперь, если мы сравним id (a.temp) и id (A.temp), они будут разными
Таким образом, почти каждый ответ здесь, кажется, пропустит конкретный момент. Переменные класса никогда не становятся переменными экземпляра, как показано в приведенном ниже коде. Используя метакласс, чтобы перехватить присвоение переменной на уровне класса, мы видим, что когда a.myattr переназначается, магия метода назначения полей в классе не вызывается. Это связано с тем, что присваивание создает новую переменную экземпляра. Это поведение абсолютно не имеет ничего общего с переменной класса, как показано вторым классом, который не имеет переменных класса и все же допускает присвоение поля.
class mymeta(type):
def __init__(cls, name, bases, d):
pass
def __setattr__(cls, attr, value):
print("setting " + attr)
super(mymeta, cls).__setattr__(attr, value)
class myclass(object):
__metaclass__ = mymeta
myattr = []
a = myclass()
a.myattr = [] #NOTHING IS PRINTED
myclass.myattr = [5] #change is printed here
b = myclass()
print(b.myattr) #pass through lookup on the base class
class expando(object):
pass
a = expando()
a.random = 5 #no class variable required
print(a.random) #but it still works
IN SHORT Переменные класса НИЧЕГО НЕ делают с переменными экземпляра .
Более ясно. Они просто попадают в область поиска по экземплярам. Переменные класса фактически являются переменными экземпляра для самого объекта класса. Вы также можете иметь метаклассивные переменные , если хотите, а также потому, что сами метаклассы тоже являются объектами. Все это объект, независимо от того, используется ли он для создания других объектов или нет, поэтому не следует привязывать семантику использования других слов в классе слов. В python класс - это просто объект, который используется для определения того, как создавать другие объекты и каковы их поведение. Метаклассы - это классы, которые создают классы, чтобы еще раз проиллюстрировать этот момент.
x.list = []
, вы могли бы изменить его и не повлиять на других. Проблема, с которой вы сталкиваетесь, заключается в том, чтоx.list
иy.list
являются одним и тем же списком, поэтому, когда вы вызываете append на один, это влияет на другое. – Matt Moriarity 5 November 2009 в 15:04__init__
. – Андрей Беньковский 25 January 2016 в 19:21