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
Kirill Smelkov
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":
PyBUF_F_CONTIGUOUS
,
PyBUF_ANY_CONTIGUOUS
PyBUF_FORMAT
PyBUF_WRITABLE
void
Py_INCREF
(
object
)
...
...
@@ -33,6 +34,7 @@ cdef class array:
unicode
mode
bytes
_format
void
(
*
callback_free_data
)(
char
*
data
)
cdef
object
_memview
def
__cinit__
(
array
self
,
tuple
shape
,
Py_ssize_t
itemsize
,
format
,
mode
=
u"c"
,
bint
allocate_buffer
=
True
):
...
...
@@ -113,6 +115,7 @@ cdef class array:
info
.
strides
=
self
.
strides
info
.
suboffsets
=
NULL
info
.
itemsize
=
self
.
itemsize
info
.
readonly
=
0
if
flags
&
PyBUF_FORMAT
:
info
.
format
=
self
.
format
...
...
@@ -138,13 +141,24 @@ cdef class array:
self
.
format
=
NULL
self
.
itemsize
=
0
def
__getitem__
(
self
,
index
):
view
=
__pyx_memoryview_new
(
self
,
PyBUF_ANY_CONTIGUOUS
|
PyBUF_FORMAT
)
return
view
[
index
]
property
memview
:
@
cname
(
'__pyx_cython_array_get_memview'
)
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
):
view
=
__pyx_memoryview_new
(
self
,
PyBUF_ANY_CONTIGUOUS
|
PyBUF_FORMAT
)
view
[
index
]
=
value
def
__getitem__
(
self
,
item
):
return
self
.
memview
[
item
]
def
__setitem__
(
self
,
item
,
value
):
self
.
memview
[
item
]
=
value
@
cname
(
"__pyx_array_new"
)
...
...
@@ -165,6 +179,7 @@ import cython
# from cpython cimport ...
cdef
extern
from
"Python.h"
:
int
PyIndex_Check
(
object
)
object
PyLong_FromVoidPtr
(
void
*
)
cdef
extern
from
"pythread.h"
:
ctypedef
void
*
PyThread_type_lock
...
...
@@ -244,6 +259,8 @@ cdef indirect_contiguous = Enum("<contiguous and indirect>")
cdef
class
memoryview
(
object
):
cdef
object
obj
cdef
object
_size
cdef
object
_array_interface
cdef
PyThread_type_lock
lock
cdef
int
acquisition_count
cdef
Py_buffer
view
...
...
@@ -336,6 +353,7 @@ cdef class memoryview(object):
info
[
0
]
=
self
.
view
info
.
obj
=
self
# Some properties that have the same sematics as in NumPy
property
T
:
@
cname
(
'__pyx_memoryview_transpose'
)
def
__get__
(
self
):
...
...
@@ -343,8 +361,8 @@ cdef class memoryview(object):
transpose_memslice
(
&
result
.
from_slice
)
return
result
property
object
:
@
cname
(
'__pyx_memoryview__get__
object
'
)
property
base
:
@
cname
(
'__pyx_memoryview__get__
base
'
)
def
__get__
(
self
):
return
self
.
obj
...
...
@@ -353,23 +371,75 @@ cdef class memoryview(object):
def
__get__
(
self
):
return
tuple
([
self
.
view
.
shape
[
i
]
for
i
in
xrange
(
self
.
view
.
ndim
)])
property
strides
:
@
cname
(
'__pyx_memoryview_get_strides'
)
def
__get__
(
self
):
return
tuple
([
self
.
view
.
strides
[
i
]
for
i
in
xrange
(
self
.
view
.
ndim
)])
property
suboffsets
:
@
cname
(
'__pyx_memoryview_get_suboffsets'
)
def
__get__
(
self
):
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
):
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
):
return
"<MemoryView of %r object>"
%
(
self
.
object
.
__class__
.
__name__
,)
return
"<MemoryView of %r object>"
%
(
self
.
base
.
__class__
.
__name__
,)
@
cname
(
'__pyx_memoryview_new'
)
...
...
@@ -703,8 +773,8 @@ cdef class _memoryviewslice(memoryview):
else
:
memoryview
.
assign_item_from_object
(
self
,
itemp
,
value
)
property
object
:
@
cname
(
'__pyx_memoryviewslice__get__
object
'
)
property
base
:
@
cname
(
'__pyx_memoryviewslice__get__
base
'
)
def
__get__
(
self
):
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
slicable and transposable in the same way that the slices are. They can also be converted back to typed
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
============
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::
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.
The arrays are indexable and slicable from Python space just like memoryview objects. If you need to do this
a lot, you're better off creating a memoryview object from your array::
The arrays are indexable and slicable from Python space just like memoryview objects, and have the same
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
memview = myslice
Of course, you are not restricted to using NumPy's type (such as ``np.int32_t`` here), you can use any usable type.
The future
==========
In the future some functionality may be added for convenience, like
1. A numpy-like `.flat` attribute (that allows efficient iteration)
2.
A numpy-like `.reshape` method
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
2.
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():
cdef
int
[:,
::
1
]
mslice
=
<
int
[:
10
,
:
10
]
>
getp
()
print
mslice
[
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):
cdef
get_default_format
(
self
):
return
b"Zg"
def
print_offsets
(
*
args
,
size
=
0
,
newline
=
True
):
"""
print with a trailing comma does not have the same semantics in python 3.
Use sys.stdout.write instead.
# 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_offsets
(
*
args
,
size
,
newline
=
True
):
sys
.
stdout
.
write
(
' '
.
join
([
str
(
item
//
size
)
for
item
in
args
]))
if
newline
:
sys
.
stdout
.
write
(
'
\
n
'
)
def
print_int_offsets
(
*
args
,
newline
=
True
):
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
cimport
numpy
as
np
import
numpy
as
np
include
"cythonarrayutil.pxi"
ctypedef
np
.
int32_t
dtype_t
def
get_array
():
...
...
@@ -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
]
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