- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在尝试子类化 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)
据我所知,它初始化正确。但我无法使用 += 运算符就地修改自身。我已经尝试覆盖 __add__
、__radd__
、__iadd__
和各种其他配置。使用 return
语句,我设法让它返回正确附加的 MyString
的新实例,但不是就地修改。成功看起来像:
b = MyString('g')
b.write('h') # b should now be 'gh'
有什么想法吗?
为了可能添加某人可能想要这样做的原因,我遵循了创建以下在内部使用纯字符串的可变类的建议:
class StringInside(object):
def __init__(self, data=''):
self.data = data
def write(self, data):
self.data += data
def read(self):
return self.data
并用 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
差异随着 number
的增加而迅速增加 - 在 100 万次交互时,StringInside
花费的时间比我愿意等待返回的时间长,而纯 str
版本在 ~100 毫秒内返回。
为了后代,我决定编写一个包含 C++ 字符串的 cython 类,以查看与松散地基于下面 Mike Mü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])
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
性能:
写作
>>> 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
读书
>>> 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
弹出
>>> # 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
最佳答案
这是对更新问题的回答。
您可以使用列表来保存数据,并且只在读取时构造字符串:
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)
这个类的表现:
%%timeit arr = StringInside()
arr.write('1234567890')
1000000 loops, best of 3: 352 ns per loop
更接近原生 str
:
%%timeit str_arr = ''
str_arr+='1234567890'
1000000 loops, best of 3: 222 ns per loop
与你的版本比较:
%%timeit arr = StringInsidePlusEqual()
arr.write('1234567890')
100000 loops, best of 3: 87 µs per loop
长期以来,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
CPython 2.7.11
>>> import timeit
>>> timeit.timeit("arr+='1234567890'", setup="arr = ''", number=10000)
0.002151966094970703
此版本支持切片:
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])
你可以用正常的方式切片:
>>> arr = StringInside('abcdefg')
>>> arr[2]
'c'
>>> arr[1:3]
'bc'
现在,read()
还支持可选的开始和停止索引:
>>> arr.read()
'abcdefg'
>>> arr.read(1, 3)
'bc'
>>> arr.read(1)
'bcdefg'
关于python - 子类 str,并创建与 += 效果相同的新方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34832106/
如果有人能为我分解它,让我能理解它,我会非常感激。我知道它用于通过 apply 方法创建新对象。 Function.prototype.new = function () { var args
新版本的HADOOP中有一个方法。http://hadoop.apache.org/docs/current/api/org/apache/hadoop/fs/FileSystem.html#conc
根据支持库变更日志和 Fragment 类文档 ( https://developer.android.com/reference/android/support/v4/app/Fragment.ht
根据支持库更改日志和 Fragment 类文档 (https://developer.android.com/reference/android/support/v4/app/Fragment.htm
执行Async BigJob() 的无限运行任务的正确方法是什么?并且可以根据要求停止 提示:我正在尝试学习 [一种] 新方法来更新我现有的策略。 我有一个简单的程序(测试程序),它有一个开始 和停止
我将解释我的想法:我使用 python 作为谷歌应用程序引擎 + js + css 主项目将存储在 src 文件夹下,如下所示:\src \app <--- 这里是 gae 的所有 python 应用
我是一名优秀的程序员,十分优秀!