ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

python – ctypes为c_ulong重新实现rshift

2019-07-03 21:43:45  阅读:619  来源: 互联网

标签:python operator-overloading bit-shift ctypes


我通过ctypes访问C库,我遇到了以下问题:

我正在使用ctypeslib生成一个“包装器”(使用ctypes访问库的ctypes命令).
C库包含在此步骤中转换为python函数的宏.
(为了尽可能独立于库内部,我想在python中使用这些宏中的一些.)

其中一个宏看起来像这样:

# using the ctypes types
myuint16_t = c_ushort
myuint32_t = c_ulong

def mymacro(x): return (myuint16_t)((myuint32_t)(x) >> 16) # macro

我想以下列方式在一个单独的模块中使用生成的函数(在函数内部):

return wrapper.mymacro(valueToBeConverted) # valueToBeConverted is an int

但使用此行我收到以下错误:

....   
def mymacro(x): return (myuint16_t)((myuint32_t)(x) >> 16) # macro
TypeError: unsupported operand type(s) for >>: 'c_ulong' and 'int'

(我知道转移c_ulong的常用方法是c_ulongvar.value>> x但是每次C库中的某些更改时我都必须修补生成的包装.所以我尽量避免这种情况).

似乎c_ulong的__rshift__实现不能在这里使用.

print c_ulong.__rshift__
# throws AttributeError: type object 'c_ulong' has no attribute '__rshift__'

嗯,看起来很奇怪……所以我决定重新实现c_ulong的__rshift__方法让它工作:

from ctypes import *
from types import MethodType

def rshift(self, val):
    print self.value >> val

# create an unbound method which applies to all (even existing) instances
c_ulong.__rshift__ = MethodType(rshift, None, c_ulong)

a = c_ulong(1)
a >> 16

但它并没有解决问题.我仍然收到一个错误:

a >> 16
TypeError: unsupported operand type(s) for >>: 'c_ulong' and 'int'

是否有可能__rshift__方法只能用于同一个类的两个实例?我尝试了以下方法:

def rshift(self, val):
    print self.value >> int(val.value)

a = c_ulong(1)
a >> c_ulong(16) 

它的工作原理.但这也意味着我仍然需要修补生成的包装器.

所以:有人知道这里的诀窍吗?

更新:

@eryksun的解决方案奏效了.我在用:

from ctypes import *
# from types import MethodType

def _rshift(self, other):
    if hasattr(other, 'value'):
        other = other.value
    return c_ulong(self.value >> other)

def _lshift(self, other):
    if hasattr(other, 'value'):
        other = other.value
    return c_ulong(self.value << other)

def _coerce(self, other):
    try:
        return self, self.__class__(other)
    except TypeError:
        return NotImplemented

# Add the functions to the type. A method is created when
# accessed as an attribute of an instance.
c_ulong.__lshift__ = _lshift
c_ulong.__rshift__ = _rshift
c_ulong.__coerce__ = _coerce

解决方法:

由于_ctypes._SimpleCData类型没有Py_TPFLAGS_CHECKTYPES标志,因此2.x子类被视为在二进制操作中使用__coerce__的旧式数字.有关调用方案和函数binary_op1中的实现,请参见Objects/abstract.c.

出于演示目的,可以在类型对象上切换此标志,您只需要定义(隐含很多void *)直到tp_flags字段.

破解PyTypeObject

from ctypes import *
import _ctypes

Py_TPFLAGS_CHECKTYPES = 1 << 4

class PyTypeObject(Structure):
    _fields_ = (('ob_refcnt', c_ssize_t),
                ('ob_type', c_void_p),
                ('ob_size', c_ssize_t),
                ('tp_name', c_char_p),
                ('tp_basicsize', c_ssize_t),
                ('tp_itemsize', c_ssize_t),
                ('tp_dealloc', c_void_p),
                ('tp_print', c_void_p),
                ('tp_getattr', c_void_p),
                ('tp_setattr', c_void_p),
                ('tp_compare', c_void_p),
                ('tp_repr', c_void_p),
                ('tp_as_number', c_void_p),
                ('tp_as_sequence', c_void_p),
                ('tp_as_mapping', c_void_p),
                ('tp_hash', c_void_p),
                ('tp_call', c_void_p),
                ('tp_str', c_void_p),
                ('tp_getattro', c_void_p),
                ('tp_setattro', c_void_p),
                ('tp_as_buffer', c_void_p),
                ('tp_flags', c_long))

接下来,创建一个unsigned long子类,并使用from_address工厂为它创建一个PyTypeObject.获取内置id的地址,这是CPython特有的实现细节:

class c_ulong(_ctypes._SimpleCData):
    _type_ = "L"

    def __rshift__(self, other):
        print '__rshift__', self, other
        if hasattr(other, 'value'):
            other = other.value
        return c_ulong(self.value >> other)

c_ulong_type = PyTypeObject.from_address(id(c_ulong))

演示

>>> a = c_ulong(16)
>>> b = c_ulong(2)

>>> a >> b
__rshift__ c_ulong(16L) c_ulong(2L)
c_ulong(4L)

>>> a >> 2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for >>: 'c_ulong' and 'int'

最后一步失败了.现在设置标志:

>>> c_ulong_type.tp_flags |= Py_TPFLAGS_CHECKTYPES

>>> a >> 2
__rshift__ c_ulong(16L) 2
c_ulong(4L)

问题解决了?但这是一个黑客.再次尝试使用__coerce__.

实施__coerce__

class c_ulong(_ctypes._SimpleCData):
    _type_ = "L"

    def __rshift__(self, other):
        print '__rshift__', self, other
        if hasattr(other, 'value'):
            other = other.value
        return c_ulong(self.value >> other)

    def __coerce__(self, other):
        print '__coerce__', self, other
        try:
            return self, self.__class__(other)
        except TypeError:
            return NotImplemented

演示

>>> a = c_ulong(16)
>>> b = c_ulong(2)

>>> a >> 2
__coerce__ c_ulong(16L) 2
__rshift__ c_ulong(16L) c_ulong(2L)
c_ulong(4L)

>>> 16 >> b
__coerce__ c_ulong(2L) 16
__rshift__ c_ulong(16L) c_ulong(2L)
c_ulong(4L)

当然,如果无法创建c_ulong,它会失败,例如浮点数:

>>> a >> 2.0
__coerce__ c_ulong(16L) 2.0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for >>: 'c_ulong' and 'float'

标签:python,operator-overloading,bit-shift,ctypes
来源: https://codeday.me/bug/20190703/1370749.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有