Существует известная проблема когда дело доходит до использования типов значения.NET в IronPython. Это недавно вызвало меня головная боль при попытке использовать Python в качестве встроенного языка сценариев в C#. Проблеме можно подвести итог следующим образом:
Учитывая структуру C#, такую как:
struct Vector {
public float x;
public float y;
}
И класс C#, такой как:
class Object {
public Vector position;
}
Следующее произойдет в IronPython:
obj = Object()
print obj.position.x # prints ‘0’
obj.position.x = 1
print obj.position.x # still prints ‘0’
Как в статье говорится, это означает, что типы значения главным образом неизменны. Однако это - проблема, поскольку я был планированием пользования векторной библиотекой, которая реализована, как замечено выше. Есть ли какие-либо обходные решения для работы с существующими библиотеками, которые полагаются на типы значения? Изменение библиотеки было бы самым последним курортом, но я скорее избегу этого.
Нет необходимости изменять библиотеку, просто используйте прокси.
struct Vector {
public float X;
public float Y;
}
class BetterVector {
public float X;
public float Y;
public Vector Optimized { get { return new Vector(X, Y); } }
}
class Object {
public BetterVector Position { get; set; }
}
Теперь код Python может устанавливать поля как обычно, и ваш код может вызывать Optimized
, когда ему нужно передать данные в OpenGL или XNA или что-то еще, что вы используете.
Вы даже можете использовать неявное принуждение, если вызов Optimized
кажется слишком трудоемким:
class BetterVector {
// ...
public static implicit operator Vector(BetterVector v) {
return v.Optimized;
}
}
Когда вы вызываете
obj.position.x = 1
Вы получаете то, что объект obj получает экземпляр структуры позиции, которую он фактически копирует, поэтому, устанавливая значение X не распространяется.
Вы говорите, что obj.position = Vector (1,0) - это то, что вам следует делать. Аналогичные вещи происходят в C #.
Edit - возможен обходной путь.
Если вы не хотите настраивать конструктор, я считаю, что это сработает:
obj = Object()
pos = obj.position; # gets the object
pos.x = 1
pos.y = 2
obj.position = pos # I'm not sure if this line is necessary
Я нашел единственный способ обновить структуры - это использовать тот факт, что вы можете указать любое публичное поле / свойство при создании структуры. Синтаксис выглядит как именованные / необязательные параметры в Python.
namespace StructExample
{
public struct MyStruct
{
public int x;
public int y { get; set; }
}
public class MyClass
{
public MyStruct a;
}
}
Мы можем использовать классы в IronPython следующим образом:
>>> foo = MyClass()
>>> print "%d:%d" % (foo.a.x, foo.a.y)
0:0
>>> foo.a.x = 1 # doesn't work!
>>> print "%d:%d" % (foo.a.x, foo.a.y)
0:0
>>> foo.a = MyStruct(x=1,y=2)
>>> print "%d:%d" % (foo.a.x, foo.a.y)
1:2
Было бы неплохо, если бы у Python был синтаксис наподобие F # 'with' для создания новой структуры, копирования полей из старой. К сожалению, при клонировании структуры нам приходится указывать все поля.
Непонятно, что вы оказались в таком углу. В python (пробовал это на IronPython 2.6.1, .Net 4.0) эквивалентный код будет примерно таким:
>>> class a:
... x = 0
... y = 0
...
>>> class b:
... Vector = a()
...
>>> c = b()
>>> c.Vector.x = 1
>>> print c.Vector.x
1
Обратите внимание, есть одно различие между моим псевдокодом и вашим - статическому свойству присваивается экземпляр класса, а не только осталось с определением типа. В качестве побочного эффекта фактический экземпляр класса инициализируется как b.Vector при создании экземпляра b.
(Псевдокод все еще "сломан" - инициализация должна идти в def init (self), но это другая история)
Мораль примера, вместо того, чтобы оставить " public Vector position "неинициализирован, встроить инициализацию" position "в класс Object.