Doge log

Abby CTO 雑賀 力王のオフィシャルサイトです

listにjoinを追加してみた

もちろん、ご期待どおりあらびきな方法です。

src

高度に訓練されたpythonistaは継承で拡張なんてしません。

setup.py
from distutils.core import setup, Extension

module1 = Extension('listjoin', sources = ['listjoin.c'])

setup(
        name='PackageName',
        version='1.0',
        description='This is a demo package', 
        ext_modules = [module1]) 
listjoin.c
#include <Python.h>

static PyMethodDef ListMethods[] = { 
    {NULL, NULL, 0, NULL} /* Sentinel */ 
}; 
        
static PyObject *
string_join(PyStringObject *self, PyObject *orig)
{
	char *sep = PyString_AS_STRING(self);
	const Py_ssize_t seplen = PyString_GET_SIZE(self);
	PyObject *res = NULL;
	char *p;
	Py_ssize_t seqlen = 0;
	size_t sz = 0;
	Py_ssize_t i;
	PyObject *seq, *item;

	seq = PySequence_Fast(orig, "");
	if (seq == NULL) {
		return NULL;
	}

	seqlen = PySequence_Size(seq);
	if (seqlen == 0) {
		Py_DECREF(seq);
		return PyString_FromString("");
	}
	if (seqlen == 1) {
		item = PySequence_Fast_GET_ITEM(seq, 0);
		if (PyString_CheckExact(item) || PyUnicode_CheckExact(item)) {
			Py_INCREF(item);
			Py_DECREF(seq);
			return item;
		}
	}

	for (i = 0; i < seqlen; i++) {
		const size_t old_sz = sz;
		item = PySequence_Fast_GET_ITEM(seq, i);
		if (!PyString_Check(item)){
			if (PyUnicode_Check(item)) {
				/* Defer to Unicode join.
				 * CAUTION:  There's no gurantee that the
				 * original sequence can be iterated over
				 * again, so we must pass seq here.
				 */
				PyObject *result;
				result = PyUnicode_Join((PyObject *)self, seq);
				Py_DECREF(seq);
				return result;
			}
            item = Py_TYPE(item)->tp_str(item);
		}
		sz += PyString_GET_SIZE(item);
		if (i != 0)
			sz += seplen;
		if (sz < old_sz || sz > PY_SSIZE_T_MAX) {
			PyErr_SetString(PyExc_OverflowError,
				"join() result is too long for a Python string");
			Py_DECREF(seq);
			return NULL;
		}
	}

	/* Allocate result space. */
	res = PyString_FromStringAndSize((char*)NULL, sz);
	if (res == NULL) {
		Py_DECREF(seq);
		return NULL;
	}

	/* Catenate everything. */
	p = PyString_AS_STRING(res);
	for (i = 0; i < seqlen; ++i) {
		size_t n;
		item = PySequence_Fast_GET_ITEM(seq, i);
		if (!PyString_Check(item)){
            item = Py_TYPE(item)->tp_str(item);
        }
		n = PyString_GET_SIZE(item);
		Py_MEMCPY(p, PyString_AS_STRING(item), n);
		p += n;
		if (i < seqlen - 1) {
			Py_MEMCPY(p, sep, seplen);
			p += seplen;
		}
	}

	Py_DECREF(seq);
	return res;
}

static PyObject *
listjoin(PyListObject *self, PyObject *sep)
{

	assert(sep != NULL && PyString_Check(sep));
	assert(x != NULL);
	return string_join((PyStringObject *)sep, self);
}

static PyMethodDef method = {"join", (PyCFunction)listjoin, METH_O, NULL};

PyMODINIT_FUNC 
initlistjoin(void) 
{ 
    PyObject *m; 
    PyTypeObject *listobj; 
    PyObject *dict; 
    PyMethodDescrObject *descr;

    m = Py_InitModule("listjoin", ListMethods); 
    listobj = &PyList_Type;
    dict = listobj->tp_dict;
    descr = PyDescr_NewMethod(listobj, &method);
    PyDict_SetItemString(dict, "join", (PyObject *)descr);

}

build & install

python setup.py build
# あるいは
python setup.py install

モジュールlistjoin.soができあがる。

実行

モジュールなのでimportします。

>>> import listjoin
>>> [1].join('')
'1'
>>> [1].join(',')
'1'
>>> [1, 2].join(',')
'1,2'
>>> [1, 2].join('-')
'1-2'
>>> ["1", "2"].join(',')
'1,2'
>>> ["1", "2"].join('-')
'1-2'
>>> ["1", "2", 3].join('-')
'1-2-3'
>>>

文字列への変換も自動(__str__表現)

とりあえず、かたつむりの林はおもしろいって話ですよねー。