2015-02-26 7 views
9

классов Python может иметь атрибуты класса:Поддерживает ли типы расширения Cython атрибуты класса?

class Foo(object): 
    bar = 4 

Есть ли аналогичная конструкция для определения класса атрибутов типов расширения Cython? Например, когда я пытаюсь скомпилировать следующий код Cython

cdef class Foo: 
    cdef int bar 
    bar = 4 

я получаю эту ошибку:

thing.c:773:3: error: use of undeclared identifier 'bar' 
    bar = 4; 
^
1 error generated. 
error: command 'cc' failed with exit status 1 
+1

Вы также должны сделать его общедоступным для доступа к нему из python 'cdef public int bar' –

+0

Спасибо за ответ. Я бы не подумал, что мой случай использования будет рассматриваться как добавление атрибутов во время работы. Я хочу определить доступные атрибуты класса один раз во время компиляции. – Pwnosaurus

+0

жаль, что не обращал внимания. Я никогда не видел атрибут класса в cython. –

ответ

1

Короткий ответ: да и нет.

Нет, нет удобной синтаксической идиомы для быстрой вставки атрибута класса в cdef class. Однако ....

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

Например, предположим, что я делаю калькулятор как класс, и хочу глобально установить точность возвращаемого значения для всех экземпляров. Я хочу дефолт во время компиляции, и время от времени я могу настроить его ниже, чтобы быстро обработать некоторые испытания, а затем настроить его для моей последней работы. Атрибут класса на заказ, но вы можете получить необходимую функциональность в cython следующим образом:

Во-первых, определить на уровне модуля следующее:

cdef int _precision[1] # storage for my class 'attribute' 
_precision[0]=8   # my default value, set during compilation 

Использование массива позволяет использовать cython idiom precision[0], что эквивалентно C *precision. Имя cdefprecision является неявным указателем, поскольку элемент данных представляет собой массив. Это позволяет использовать синтаксические идиомы cython для конвертации из cython мест хранения в ссылки на питоны. Если все, что вам нужно, это глобальная константа, к которой может обращаться код cdef в любом из классов модуля, все готово. Если вы хотите использовать его строго как атрибут класса, вы должны обеспечить соблюдение этой дисциплины - компилятору все равно.

Теперь, если вы хотите, чтобы настроить значение от python кода, вам потребуется пара cdef функций, python код в модуле может позвонить, чтобы получить доступ к атрибуту «»:

cdef int* get_precision(): return _precision 
cdef void* set_precision(int i): _precision[0]=i 

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

cdef class SomeCalculator: 
    ... 

    property precision: 
    def __get__(self): 
     """Get or set calculation precision, default == 8. 
     This is like a class attribute: setting affects all instances, 
     however, it also affects all subclasses.""" 
     return get_precision()[0] 
    def __set__(self,int integer): set_precision(min(30,max(0,integer))) 

Первый получает ссылку питона в «атрибут». Второй устанавливает «атрибут» с интегральным значением python, находящимся под контролем. Интерфейс вызова и возврата функции cython автоматически выполняет преобразования, которые сложнее, чем они выглядят.

Например, get_precision возвращает C-pointer. Если вы сделали разыменование в get_precision, вы получили бы ошибку, пытающуюся вернуть C-int в __get__, как если бы это было python. Если вместо этого вы просто опустили разыменование [0] в __get__, вы получили бы ошибку, пытающуюся вернуть C-pointer, как если бы это был python int. Как написано, автоматические преобразования корректно соответствуют типам. cython очень тонкий в отношении такого рода вещей и может молча возвращать неверные значения, обнаруживаемые только во время выполнения. Может потребоваться некоторое экспериментирование, чтобы вывести правильное заклинание.

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

Несмотря на это, есть и другие отличия. Атрибут реального класса может ссылаться на класс или экземпляр. Это свойство может ссылаться только на экземпляр. Установка атрибута реального класса в экземпляре создает экземпляр конкретной копии, оставляя атрибут класса нетронутым, но невидимым для измененного экземпляра.

Для данного прецедента это работает. Атрибут реального класса не нужен. Поскольку код cython обычно менее абстрактен и вычисляется интенсивным, этого минимального подхода достаточно часто.

1

Вы не можете сделать это таким образом. Я не знаю, поддерживаются ли статические атрибуты, но «обычные» должны быть доступны из методов, например. конструктор:

cdef class Foo: 
    cdef int bar 
    def __init__(self): 
     self.bar = 4 
5

Хотя это не представляется возможным, чтобы иметь C-типизированных статические атрибуты, типы расширений Cython могут иметь регулярные Python статические атрибуты, которые автоматически доступны в Python. Просто объявите их, как вы бы объявить их в Python:

cdef class Foo: 
    bar = 4 

сгенерированный код показывает, что эти статические атрибуты хранятся в виде объектов Python в атрибуте Dict из класса объекта, то есть, если вы используете их в контекстах, где C- типы используются, они преобразуются обратно из объектов Python.

+1

На самом деле, я подозреваю, что ничего не похоже на атрибут статического класса в cpython * except * в качестве элемента его '__dict__'; compare: 'type (« MyType », (type,), {'__slots __' :('foo',)})' – SingleNegationElimination

+1

похоже, что они доступны только для чтения. Есть ли способ сделать общедоступным атрибуты класса в расширении Cython? – Zephyr

+0

определяют геттер и сеттер для них –

Смежные вопросы