dom*_*ato 5 python string subclass immutability built-in
我正在尝试子类str- 不是为了重要的东西,只是一个实验来了解有关Python内置类型的更多信息.我已经按照str这种方式进行了子类化(使用__new__因为str是不可变的):
class MyString(str):
def __new__(cls, value=''):
return str.__new__(cls, value)
def __radd__(self, value): # what method should I use??
return MyString(self + value) # what goes here??
def write(self, data):
self.__radd__(data)
Run Code Online (Sandbox Code Playgroud)
据我所知,它初始化正确.但我无法使用+ =运算符进行就地修改.我试图压倒一切的__add__,__radd__,__iadd__以及其他各种配置.使用return语句,香港专业教育学院设法让它返回正确附加的新实例MyString,但不是就地修改.成功看起来像:
b = MyString('g')
b.write('h') # b should now be 'gh'
Run Code Online (Sandbox Code Playgroud)
有什么想法吗?
为了可能添加某人可能想要这样做的原因,我遵循了创建以下在内部使用普通字符串的可变类的建议:
class StringInside(object):
def __init__(self, data=''):
self.data = data
def write(self, data):
self.data += data
def read(self):
return self.data
Run Code Online (Sandbox Code Playgroud)
并使用timeit进行测试:
timeit.timeit("arr+='1234567890'", setup="arr = ''", number=10000)
0.004415035247802734
timeit.timeit("arr.write('1234567890')", setup="from hard import StringInside; arr = StringInside()", number=10000)
0.0331270694732666
Run Code Online (Sandbox Code Playgroud)
差异在上升时迅速number增加 - 在100万次交互中,StringInside花费的时间比我愿意等待返回的时间长,而纯str版本在~100ms内返回.
对于后人,我决定编写一个包含C++字符串的cython类,看看性能是否可以提高,相比之下基于MikeMüller的更新版本松散,我成功了.我意识到cython是"作弊"但我提供这只是为了好玩.
python版本:
class Mike(object):
def __init__(self, data=''):
self._data = []
self._data.extend(data)
def write(self, data):
self._data.extend(data)
def read(self, stop=None):
return ''.join(self._data[0:stop])
def pop(self, stop=None):
if not stop:
stop = len(self._data)
try:
return ''.join(self._data[0:stop])
finally:
self._data = self._data[stop:]
def __getitem__(self, key):
return ''.join(self._data[key])
Run Code Online (Sandbox Code Playgroud)
cython版本:
from libcpp.string cimport string
cdef class CyString:
cdef string buff
cdef public int length
def __cinit__(self, string data=''):
self.length = len(data)
self.buff = data
def write(self, string new_data):
self.length += len(new_data)
self.buff += new_data
def read(self, int length=0):
if not length:
length = self.length
return self.buff.substr(0, length)
def pop(self, int length=0):
if not length:
length = self.length
ans = self.buff.substr(0, length)
self.buff.erase(0, length)
return ans
Run Code Online (Sandbox Code Playgroud)
性能:
写作
>>> timeit.timeit("arr.write('1234567890')", setup="from pyversion import Mike; arr = Mike()", number=1000000)
0.5992741584777832
>>> timeit.timeit("arr.write('1234567890')", setup="from cyversion import CyBuff; arr = CyBuff()", number=1000000)
0.17381906509399414
Run Code Online (Sandbox Code Playgroud)
读
>>> timeit.timeit("arr.write('1234567890'); arr.read(5)", setup="from pyversion import Mike; arr = Mike()", number=1000000)
1.1499049663543701
>>> timeit.timeit("arr.write('1234567890'); arr.read(5)", setup="from cyversion import CyBuff; arr = CyBuff()", number=1000000)
0.2894480228424072
Run Code Online (Sandbox Code Playgroud)
啪
>>> # note I'm using 10e3 iterations - the python version wouldn't return otherwise
>>> timeit.timeit("arr.write('1234567890'); arr.pop(5)", setup="from pyversion import Mike; arr = Mike()", number=10000)
0.7390561103820801
>>> timeit.timeit("arr.write('1234567890'); arr.pop(5)", setup="from cyversion import CyBuff; arr = CyBuff()", number=10000)
0.01501607894897461
Run Code Online (Sandbox Code Playgroud)
这是更新问题的答案.
您可以使用列表来保存数据,并在读取时仅构造字符串:
class StringInside(object):
def __init__(self, data=''):
self._data = []
self._data.append(data)
def write(self, data):
self._data.append(data)
def read(self):
return ''.join(self._data)
Run Code Online (Sandbox Code Playgroud)
这堂课的表现:
%%timeit arr = StringInside()
arr.write('1234567890')
1000000 loops, best of 3: 352 ns per loop
Run Code Online (Sandbox Code Playgroud)
更接近原生的str:
%%timeit str_arr = ''
str_arr+='1234567890'
1000000 loops, best of 3: 222 ns per loop
Run Code Online (Sandbox Code Playgroud)
与您的版本比较:
%%timeit arr = StringInsidePlusEqual()
arr.write('1234567890')
100000 loops, best of 3: 87 µs per loop
Run Code Online (Sandbox Code Playgroud)
my_string += another_string长期以来,构建弦乐的方式一直是反模式的表现.CPython对这种情况有一些优化.似乎CPython无法检测到此处使用此模式.这可能是因为它有点隐藏在一个类中.
由于各种原因,并非所有实现都具有此优化.例如.PyPy,通常比CPython快得多,对于这个用例来说要慢得多:
PyPy 2.6.0(Python 2.7.9)
>>>> import timeit
>>>> timeit.timeit("arr+='1234567890'", setup="arr = ''", number=10000)
0.08312582969665527
Run Code Online (Sandbox Code Playgroud)
CPython 2.7.11
>>> import timeit
>>> timeit.timeit("arr+='1234567890'", setup="arr = ''", number=10000)
0.002151966094970703
Run Code Online (Sandbox Code Playgroud)
此版本支持切片:
class StringInside(object):
def __init__(self, data=''):
self._data = []
self._data.extend(data)
def write(self, data):
self._data.extend(data)
def read(self, start=None, stop=None):
return ''.join(self._data[start:stop])
def __getitem__(self, key):
return ''.join(self._data[key])
Run Code Online (Sandbox Code Playgroud)
你可以正常切片:
>>> arr = StringInside('abcdefg')
>>> arr[2]
'c'
>>> arr[1:3]
'bc'
Run Code Online (Sandbox Code Playgroud)
现在,read()还支持可选的启动和停止索引:
>>> arr.read()
'abcdefg'
>>> arr.read(1, 3)
'bc'
>>> arr.read(1)
'bcdefg'
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
906 次 |
| 最近记录: |