Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Xavier Thompson
cython
Commits
71064916
Commit
71064916
authored
Sep 06, 2011
by
Mark Florisson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support memoryview(slice) -> NumPy coercion + NumPy-like attributes
parent
dca00ef2
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
156 additions
and
41 deletions
+156
-41
Cython/Utility/MemoryView.pyx
Cython/Utility/MemoryView.pyx
+84
-14
docs/src/userguide/memoryviews.rst
docs/src/userguide/memoryviews.rst
+27
-9
tests/run/cythonarray.pyx
tests/run/cythonarray.pyx
+6
-0
tests/run/mockbuffers.pxi
tests/run/mockbuffers.pxi
+4
-18
tests/run/numpy_memoryview.pyx
tests/run/numpy_memoryview.pyx
+35
-0
No files found.
Cython/Utility/MemoryView.pyx
View file @
71064916
...
@@ -11,6 +11,7 @@ cdef extern from "Python.h":
...
@@ -11,6 +11,7 @@ cdef extern from "Python.h":
PyBUF_F_CONTIGUOUS
,
PyBUF_F_CONTIGUOUS
,
PyBUF_ANY_CONTIGUOUS
PyBUF_ANY_CONTIGUOUS
PyBUF_FORMAT
PyBUF_FORMAT
PyBUF_WRITABLE
void
Py_INCREF
(
object
)
void
Py_INCREF
(
object
)
...
@@ -33,6 +34,7 @@ cdef class array:
...
@@ -33,6 +34,7 @@ cdef class array:
unicode
mode
unicode
mode
bytes
_format
bytes
_format
void
(
*
callback_free_data
)(
char
*
data
)
void
(
*
callback_free_data
)(
char
*
data
)
cdef
object
_memview
def
__cinit__
(
array
self
,
tuple
shape
,
Py_ssize_t
itemsize
,
format
,
def
__cinit__
(
array
self
,
tuple
shape
,
Py_ssize_t
itemsize
,
format
,
mode
=
u"c"
,
bint
allocate_buffer
=
True
):
mode
=
u"c"
,
bint
allocate_buffer
=
True
):
...
@@ -113,6 +115,7 @@ cdef class array:
...
@@ -113,6 +115,7 @@ cdef class array:
info
.
strides
=
self
.
strides
info
.
strides
=
self
.
strides
info
.
suboffsets
=
NULL
info
.
suboffsets
=
NULL
info
.
itemsize
=
self
.
itemsize
info
.
itemsize
=
self
.
itemsize
info
.
readonly
=
0
if
flags
&
PyBUF_FORMAT
:
if
flags
&
PyBUF_FORMAT
:
info
.
format
=
self
.
format
info
.
format
=
self
.
format
...
@@ -138,13 +141,24 @@ cdef class array:
...
@@ -138,13 +141,24 @@ cdef class array:
self
.
format
=
NULL
self
.
format
=
NULL
self
.
itemsize
=
0
self
.
itemsize
=
0
def
__getitem__
(
self
,
index
):
property
memview
:
view
=
__pyx_memoryview_new
(
self
,
PyBUF_ANY_CONTIGUOUS
|
PyBUF_FORMAT
)
@
cname
(
'__pyx_cython_array_get_memview'
)
return
view
[
index
]
def
__get__
(
self
):
# Make this a property as 'data' may be set after instantiation
if
self
.
_memview
is
None
:
flags
=
PyBUF_ANY_CONTIGUOUS
|
PyBUF_FORMAT
|
PyBUF_WRITABLE
self
.
_memview
=
__pyx_memoryview_new
(
self
,
flags
)
return
self
.
_memview
def
__getattr__
(
self
,
attr
):
return
getattr
(
self
.
memview
,
attr
)
def
__setitem__
(
self
,
index
,
value
):
def
__getitem__
(
self
,
item
):
view
=
__pyx_memoryview_new
(
self
,
PyBUF_ANY_CONTIGUOUS
|
PyBUF_FORMAT
)
return
self
.
memview
[
item
]
view
[
index
]
=
value
def
__setitem__
(
self
,
item
,
value
):
self
.
memview
[
item
]
=
value
@
cname
(
"__pyx_array_new"
)
@
cname
(
"__pyx_array_new"
)
...
@@ -165,6 +179,7 @@ import cython
...
@@ -165,6 +179,7 @@ import cython
# from cpython cimport ...
# from cpython cimport ...
cdef
extern
from
"Python.h"
:
cdef
extern
from
"Python.h"
:
int
PyIndex_Check
(
object
)
int
PyIndex_Check
(
object
)
object
PyLong_FromVoidPtr
(
void
*
)
cdef
extern
from
"pythread.h"
:
cdef
extern
from
"pythread.h"
:
ctypedef
void
*
PyThread_type_lock
ctypedef
void
*
PyThread_type_lock
...
@@ -244,6 +259,8 @@ cdef indirect_contiguous = Enum("<contiguous and indirect>")
...
@@ -244,6 +259,8 @@ cdef indirect_contiguous = Enum("<contiguous and indirect>")
cdef
class
memoryview
(
object
):
cdef
class
memoryview
(
object
):
cdef
object
obj
cdef
object
obj
cdef
object
_size
cdef
object
_array_interface
cdef
PyThread_type_lock
lock
cdef
PyThread_type_lock
lock
cdef
int
acquisition_count
cdef
int
acquisition_count
cdef
Py_buffer
view
cdef
Py_buffer
view
...
@@ -336,6 +353,7 @@ cdef class memoryview(object):
...
@@ -336,6 +353,7 @@ cdef class memoryview(object):
info
[
0
]
=
self
.
view
info
[
0
]
=
self
.
view
info
.
obj
=
self
info
.
obj
=
self
# Some properties that have the same sematics as in NumPy
property
T
:
property
T
:
@
cname
(
'__pyx_memoryview_transpose'
)
@
cname
(
'__pyx_memoryview_transpose'
)
def
__get__
(
self
):
def
__get__
(
self
):
...
@@ -343,8 +361,8 @@ cdef class memoryview(object):
...
@@ -343,8 +361,8 @@ cdef class memoryview(object):
transpose_memslice
(
&
result
.
from_slice
)
transpose_memslice
(
&
result
.
from_slice
)
return
result
return
result
property
object
:
property
base
:
@
cname
(
'__pyx_memoryview__get__
object
'
)
@
cname
(
'__pyx_memoryview__get__
base
'
)
def
__get__
(
self
):
def
__get__
(
self
):
return
self
.
obj
return
self
.
obj
...
@@ -353,23 +371,75 @@ cdef class memoryview(object):
...
@@ -353,23 +371,75 @@ cdef class memoryview(object):
def
__get__
(
self
):
def
__get__
(
self
):
return
tuple
([
self
.
view
.
shape
[
i
]
for
i
in
xrange
(
self
.
view
.
ndim
)])
return
tuple
([
self
.
view
.
shape
[
i
]
for
i
in
xrange
(
self
.
view
.
ndim
)])
property
strides
:
property
strides
:
@
cname
(
'__pyx_memoryview_get_strides'
)
@
cname
(
'__pyx_memoryview_get_strides'
)
def
__get__
(
self
):
def
__get__
(
self
):
return
tuple
([
self
.
view
.
strides
[
i
]
for
i
in
xrange
(
self
.
view
.
ndim
)])
return
tuple
([
self
.
view
.
strides
[
i
]
for
i
in
xrange
(
self
.
view
.
ndim
)])
property
suboffsets
:
property
suboffsets
:
@
cname
(
'__pyx_memoryview_get_suboffsets'
)
@
cname
(
'__pyx_memoryview_get_suboffsets'
)
def
__get__
(
self
):
def
__get__
(
self
):
return
tuple
([
self
.
view
.
suboffsets
[
i
]
for
i
in
xrange
(
self
.
view
.
ndim
)])
return
tuple
([
self
.
view
.
suboffsets
[
i
]
for
i
in
xrange
(
self
.
view
.
ndim
)])
property
ndim
:
@
cname
(
'__pyx_memoryview_get_ndim'
)
def
__get__
(
self
):
return
self
.
view
.
ndim
property
itemsize
:
@
cname
(
'__pyx_memoryview_get_itemsize'
)
def
__get__
(
self
):
return
self
.
view
.
itemsize
property
nbytes
:
@
cname
(
'__pyx_memoryview_get_nbytes'
)
def
__get__
(
self
):
return
self
.
size
*
self
.
view
.
itemsize
property
size
:
@
cname
(
'__pyx_memoryview_get_size'
)
def
__get__
(
self
):
if
self
.
_size
is
None
:
result
=
1
for
length
in
self
.
shape
:
result
*=
length
self
.
_size
=
result
return
self
.
_size
property
__array_interface__
:
@
cname
(
'__pyx_numpy_array_interface'
)
def
__get__
(
self
):
"Interface for NumPy to obtain a ndarray from this object"
# Note: we always request writable buffers, so we set readonly to
# False in the data tuple
if
self
.
_array_interface
is
None
:
for
suboffset
in
self
.
suboffsets
:
if
suboffset
>=
0
:
raise
ValueError
(
"Cannot convert indirect buffer to numpy object"
)
self
.
_array_interface
=
dict
(
data
=
(
PyLong_FromVoidPtr
(
self
.
view
.
buf
),
False
),
shape
=
self
.
shape
,
strides
=
self
.
strides
,
typestr
=
"%s%d"
%
(
self
.
format
,
self
.
view
.
itemsize
),
version
=
3
)
return
self
.
_array_interface
def
__len__
(
self
):
if
self
.
view
.
ndim
>=
1
:
return
self
.
view
.
shape
[
0
]
return
0
def
__repr__
(
self
):
def
__repr__
(
self
):
return
"<MemoryView of %r at 0x%x>"
%
(
self
.
object
.
__class__
.
__name__
,
id
(
self
))
return
"<MemoryView of %r at 0x%x>"
%
(
self
.
base
.
__class__
.
__name__
,
id
(
self
))
def
__str__
(
self
):
def
__str__
(
self
):
return
"<MemoryView of %r object>"
%
(
self
.
object
.
__class__
.
__name__
,)
return
"<MemoryView of %r object>"
%
(
self
.
base
.
__class__
.
__name__
,)
@
cname
(
'__pyx_memoryview_new'
)
@
cname
(
'__pyx_memoryview_new'
)
...
@@ -703,8 +773,8 @@ cdef class _memoryviewslice(memoryview):
...
@@ -703,8 +773,8 @@ cdef class _memoryviewslice(memoryview):
else
:
else
:
memoryview
.
assign_item_from_object
(
self
,
itemp
,
value
)
memoryview
.
assign_item_from_object
(
self
,
itemp
,
value
)
property
object
:
property
base
:
@
cname
(
'__pyx_memoryviewslice__get__
object
'
)
@
cname
(
'__pyx_memoryviewslice__get__
base
'
)
def
__get__
(
self
):
def
__get__
(
self
):
return
self
.
from_object
return
self
.
from_object
...
...
docs/src/userguide/memoryviews.rst
View file @
71064916
...
@@ -154,6 +154,19 @@ These typed slices can be converted to Python objects (`cython.memoryview`), and
...
@@ -154,6 +154,19 @@ These typed slices can be converted to Python objects (`cython.memoryview`), and
slicable and transposable in the same way that the slices are. They can also be converted back to typed
slicable and transposable in the same way that the slices are. They can also be converted back to typed
slices at any time.
slices at any time.
They have the following attributes:
* shape
* strides
* suboffsets
* ndim
* size
* itemsize
* nbytes
And of course the aforementioned ``T`` attribute. These attributes have the same semantics as in NumPy_.
Cython Array
Cython Array
============
============
Whenever a slice is copied (using any of the `copy` or `copy_fortran` methods), you get a new
Whenever a slice is copied (using any of the `copy` or `copy_fortran` methods), you get a new
...
@@ -186,21 +199,26 @@ You can also cast pointers to arrays::
...
@@ -186,21 +199,26 @@ You can also cast pointers to arrays::
Again, when the array will go out of scope, it will free the data, unless you register a callback like above.
Again, when the array will go out of scope, it will free the data, unless you register a callback like above.
Of course, you can also immidiately assign a cython.array to a typed memoryview slice.
Of course, you can also immidiately assign a cython.array to a typed memoryview slice.
The arrays are indexable and slicable from Python space just like memoryview objects. If you need to do this
The arrays are indexable and slicable from Python space just like memoryview objects, and have the same
a lot, you're better off creating a memoryview object from your array::
attributes as memoryview objects.
Coercion to NumPy
=================
Memoryview (and array) objects can be coerced to a NumPy ndarray, without having to copy the data. You can
e.g. do::
memview = cython.memoryview(my_cython_array, PyBUF_C_CONTIGUOUS)
cimport numpy as np
import numpy as np
# OR
numpy_array = np.asarray(<np.int32_t[:10, :10]> my_pointer)
cdef int[:, ::1] myslice = my_cython_array
Of course, you are not restricted to using NumPy's type (such as ``np.int32_t`` here), you can use any usable type.
memview = myslice
The future
The future
==========
==========
In the future some functionality may be added for convenience, like
In the future some functionality may be added for convenience, like
1. A numpy-like `.flat` attribute (that allows efficient iteration)
1. A numpy-like `.flat` attribute (that allows efficient iteration)
2.
A numpy-like `.reshape` method
2.
Indexing with newaxis or None to introduce a new axis
3. A method `to_numpy` which would convert a memoryview object to a NumPy object
4. Indexing with newaxis or None to introduce a new axis
.. _NumPy: http://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html#memory-layout
tests/run/cythonarray.pyx
View file @
71064916
...
@@ -144,3 +144,9 @@ def test_array_from_pointer():
...
@@ -144,3 +144,9 @@ def test_array_from_pointer():
cdef
int
[:,
::
1
]
mslice
=
<
int
[:
10
,
:
10
]
>
getp
()
cdef
int
[:,
::
1
]
mslice
=
<
int
[:
10
,
:
10
]
>
getp
()
print
mslice
[
5
,
6
]
print
mslice
[
5
,
6
]
print
(
<
int
[:
12
,
:
10
]
>
getp
(
12
,
10
))[
5
,
6
]
print
(
<
int
[:
12
,
:
10
]
>
getp
(
12
,
10
))[
5
,
6
]
# There is a reference cycle between the array object to its memoryview
# object that it keeps
del
c_arr
import
gc
gc
.
collect
()
tests/run/mockbuffers.pxi
View file @
71064916
...
@@ -331,24 +331,10 @@ cdef class LongComplexMockBuffer(MockBuffer):
...
@@ -331,24 +331,10 @@ cdef class LongComplexMockBuffer(MockBuffer):
cdef
get_default_format
(
self
):
return
b"Zg"
cdef
get_default_format
(
self
):
return
b"Zg"
def
print_offsets
(
*
args
,
size
=
0
,
newline
=
True
):
def
print_offsets
(
*
args
,
size
,
newline
=
True
):
"""
sys
.
stdout
.
write
(
' '
.
join
([
str
(
item
//
size
)
for
item
in
args
]))
print with a trailing comma does not have the same semantics in python 3.
if
newline
:
Use sys.stdout.write instead.
sys
.
stdout
.
write
(
'
\
n
'
)
# python 2
->> print 'foo',; sys.stdout.write('bar
\
n
') # no space between foo and bar
foobar
In python 3 you get trailing spaces from the last ','
"""
for
idx
,
item
in
enumerate
(
args
):
if
idx
==
len
(
args
)
-
1
:
sys
.
stdout
.
write
(
str
(
item
//
size
))
else
:
sys
.
stdout
.
write
(
'%s '
%
(
item
//
size
))
if
newline
:
sys
.
stdout
.
write
(
'
\
n
'
)
def
print_int_offsets
(
*
args
,
newline
=
True
):
def
print_int_offsets
(
*
args
,
newline
=
True
):
print_offsets
(
*
args
,
size
=
sizeof
(
int
),
newline
=
newline
)
print_offsets
(
*
args
,
size
=
sizeof
(
int
),
newline
=
newline
)
...
...
tests/run/numpy_memoryview.pyx
View file @
71064916
...
@@ -8,6 +8,8 @@ Test slicing for memoryviews and memoryviewslices
...
@@ -8,6 +8,8 @@ Test slicing for memoryviews and memoryviewslices
cimport
numpy
as
np
cimport
numpy
as
np
import
numpy
as
np
import
numpy
as
np
include
"cythonarrayutil.pxi"
ctypedef
np
.
int32_t
dtype_t
ctypedef
np
.
int32_t
dtype_t
def
get_array
():
def
get_array
():
...
@@ -163,3 +165,36 @@ def test_transpose():
...
@@ -163,3 +165,36 @@ def test_transpose():
print
a
[
3
,
2
],
a
.
T
[
2
,
3
],
a_obj
[
3
,
2
],
a_obj
.
T
[
2
,
3
],
numpy_obj
[
3
,
2
],
numpy_obj
.
T
[
2
,
3
]
print
a
[
3
,
2
],
a
.
T
[
2
,
3
],
a_obj
[
3
,
2
],
a_obj
.
T
[
2
,
3
],
numpy_obj
[
3
,
2
],
numpy_obj
.
T
[
2
,
3
]
def
test_coerce_to_numpy
():
"""
>>> test_coerce_to_numpy()
34
34
2
"""
cyarray
=
create_array
(
shape
=
(
8
,
5
),
mode
=
"c"
,
use_callback
=
True
)
numarray
=
np
.
asarray
(
cyarray
)
print
cyarray
[
6
,
4
]
del
cyarray
print
numarray
[
6
,
4
]
numarray
[
6
,
4
]
=
2
print
numarray
[
6
,
4
]
def
test_numpy_like_attributes
(
cyarray
):
"""
>>> cyarray = create_array(shape=(8, 5), mode="c", use_callback=True)
>>> test_numpy_like_attributes(cyarray)
>>> test_numpy_like_attributes(cyarray.memview)
"""
numarray
=
np
.
asarray
(
cyarray
)
assert
cyarray
.
shape
==
numarray
.
shape
assert
cyarray
.
strides
==
numarray
.
strides
assert
cyarray
.
ndim
==
numarray
.
ndim
assert
cyarray
.
size
==
numarray
.
size
assert
cyarray
.
nbytes
==
numarray
.
nbytes
cdef
int
[:,
:]
mslice
=
numarray
assert
(
<
object
>
mslice
).
base
is
numarray
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment