Pythonの型をPHPに移植してみた

文字列とか配列とかのスライスって使い易い!
辞書って使い易い!
って所から始まって、ついやりすぎてしまった。
 
とりあえず名前はPHPyとしておいた。
多分Strは日本語非対応。
 
意外と時間かかった…。
 

移植した型

・Tuple
・List
・Dict
・Frozenset
・Set
・Str
・Int
・Xrange
 

テストコード

Tuple
<?php
require_once('PHPy.php');

try {
	echo "### PyTuple\n\n";
	$tuple = new PyTuple('aaa', 'bbb', 'ccc', 'ddd', 'eee');

	echo "# slice\n";
	printf("'%s': %s\n", '0:3', $tuple['0:3']);
	printf("'%s': %s\n", '1:4', $tuple['1:4']);
	printf("'%s': %s\n", '-3:', $tuple['-3:']);
	printf("'%s': %s\n", '4', $tuple['4']);
	echo "\n";

	echo "# toString\n";
	echo $tuple."\n\n";

	echo "# toArray\n";
	print_r($tuple->toArray());
	echo "\n";

	echo "# iterator\n";
	foreach ($tuple as $key=>$value) {
		printf("%s: %s\n", $key, $value);
	}

} catch (PyException $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());

} catch (Exception $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());
}

 

### PyTuple

# slice
'0:3': ('aaa', 'bbb', 'ccc')
'1:4': ('bbb', 'ccc', 'ddd')
'-3:': ('ccc', 'ddd', 'eee')
'4': eee

# toString
('aaa', 'bbb', 'ccc', 'ddd', 'eee')

# toArray
Array
(
    [0] => aaa
    [1] => bbb
    [2] => ccc
    [3] => ddd
    [4] => eee
)

# iterator
0: aaa
1: bbb
2: ccc
3: ddd
4: eee

 

List
<?php
require_once('PHPy.php');

try {
	echo "### PyList\n\n";
	$list = new PyList('aaa', 'bbb', 'ccc', 'ddd', 'eee');

	echo "# slice\n";
	printf("'%s': %s\n", '0:3', $list['0:3']);
	printf("'%s': %s\n", '1:4', $list['1:4']);
	printf("'%s': %s\n", '-3:', $list['-3:']);
	printf("'%s': %s\n", '4', $list['4']);
	echo "\n";

	echo "# toString\n";
	echo $list."\n\n";

	echo "# toArray\n";
	print_r($list->toArray());
	echo "\n";

	echo "# iterator\n";
	foreach ($list as $key=>$value) {
		printf("%s: %s\n", $key, $value);
	}
	echo "\n";

	echo "# append\n";
	$list->append(new PyList('fff', 'ggg'));
	echo $list."\n\n";

	echo "# extend\n";
	$list->extend(new PyList('hhh', 'iii'));
	echo $list."\n\n";

	echo "# insert\n";
	$list->insert(4, 'jjj');
	echo $list."\n\n";

	echo "# remove\n";
	$list->remove(new PyList('fff', 'ggg'));
	echo $list."\n\n";

	echo "# pop\n";
	$list->pop();
	echo $list."\n\n";

	echo "# index\n";
	echo $list->index('ddd')."\n\n";

	echo "# count\n";
	echo $list->count('ccc')."\n\n";

	echo "# sort\n";
	echo $list->sort()."\n\n";

	echo "# reverse\n";
	echo $list->reverse()."\n\n";

} catch (PyException $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());

} catch (Exception $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());
}

 

### PyList

# slice
'0:3': ['aaa', 'bbb', 'ccc']
'1:4': ['bbb', 'ccc', 'ddd']
'-3:': ['ccc', 'ddd', 'eee']
'4': eee

# toString
['aaa', 'bbb', 'ccc', 'ddd', 'eee']

# toArray
Array
(
    [0] => aaa
    [1] => bbb
    [2] => ccc
    [3] => ddd
    [4] => eee
)

# iterator
0: aaa
1: bbb
2: ccc
3: ddd
4: eee

# append
['aaa', 'bbb', 'ccc', 'ddd', 'eee', ['fff', 'ggg']]

# extend
['aaa', 'bbb', 'ccc', 'ddd', 'eee', ['fff', 'ggg'], 'hhh', 'iii']

# insert
['aaa', 'bbb', 'ccc', 'ddd', 'jjj', 'eee', ['fff', 'ggg'], 'hhh', 'iii']

# remove
['aaa', 'bbb', 'ccc', 'ddd', 'jjj', 'eee', 'hhh', 'iii']

# pop
['aaa', 'bbb', 'ccc', 'ddd', 'jjj', 'eee', 'hhh']

# index
3

# count
1

# sort
['aaa', 'bbb', 'ccc', 'ddd', 'eee', 'hhh', 'jjj']

# reverse
['jjj', 'hhh', 'eee', 'ddd', 'ccc', 'bbb', 'aaa']

 

Dict
<?php
require_once('PHPy.php');

try {
	echo "### PyDict\n\n";
	$dict = new PyDict(array('aaa'=>'AAA', 'bbb'=>'BBB', 'ccc'=>'CCC', 'ddd'=>'DDD', 'eee'=>'EEE'));

	echo "# toString\n";
	echo $dict."\n\n";

	echo "# toArray\n";
	print_r($dict->toArray());
	echo "\n";

	echo "# copy\n";
	$dict2 = $dict->copy();
	echo $dict2."\n\n";

	echo "# clear\n";
	$dict2->clear();
	echo $dict2."\n\n";

	echo "# has_key\n";
	var_dump($dict->has_key('aaa'));
	echo "\n";

	echo "# items\n";
	echo $dict->items()."\n\n";

	echo "# keys\n";
	echo $dict->keys()."\n\n";

	echo "# update\n";
	$dict->update(array('xxx'=>'XXX', 'yyy'=>'YYY', 'zzz'=>'ZZZ'));
	echo $dict."\n\n";

	echo "# fromkeys\n";
	echo PyDict::fromkeys('abcdef', 'ABCDEF')."\n\n";

	echo "# values\n";
	echo $dict->values()."\n\n";

	echo "# setdefault\n";
	echo $dict->setdefault('ooo', 'OOO')."\n";
	echo $dict."\n\n";

	echo "# pop\n";
	echo $dict->pop('aaa', 'not found')."\n";
	echo $dict."\n\n";

	echo "# popitem\n";
	echo $dict->popitem()."\n";
	echo $dict."\n\n";

	echo "# iterator\n";
	foreach ($dict as $key=>$value) {
		printf("%s: %s\n", $key, $value);
	}
	echo "\n";

	echo "# iterator iteritems\n";
	foreach ($dict->iteritems() as $key=>$value) {
		printf("%s: %s\n", $key, $value);
	}
	echo "\n";

	echo "# iterator iterkeys\n";
	foreach ($dict->iterkeys() as $key=>$value) {
		printf("%s: %s\n", $key, $value);
	}
	echo "\n";

	echo "# iterator itervalues\n";
	foreach ($dict->itervalues() as $key=>$value) {
		printf("%s: %s\n", $key, $value);
	}


} catch (PyException $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());

} catch (Exception $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());
}

 

### PyDict

# toString
{'aaa': 'AAA', 'bbb': 'BBB', 'ccc': 'CCC', 'ddd': 'DDD', 'eee': 'EEE'}

# toArray
Array
(
    [aaa] => AAA
    [bbb] => BBB
    [ccc] => CCC
    [ddd] => DDD
    [eee] => EEE
)

# copy
{'aaa': 'AAA', 'bbb': 'BBB', 'ccc': 'CCC', 'ddd': 'DDD', 'eee': 'EEE'}

# clear
{}

# has_key
bool(true)

# items
[('aaa', 'AAA'), ('bbb', 'BBB'), ('ccc', 'CCC'), ('ddd', 'DDD'), ('eee', 'EEE')]

# keys
['aaa', 'bbb', 'ccc', 'ddd', 'eee']

# update
{'aaa': 'AAA', 'bbb': 'BBB', 'ccc': 'CCC', 'ddd': 'DDD', 'eee': 'EEE', 'xxx': 'XXX', 'yyy': 'YYY', 'zzz': 'ZZZ'}

# fromkeys
{'a': 'ABCDEF', 'b': 'ABCDEF', 'c': 'ABCDEF', 'd': 'ABCDEF', 'e': 'ABCDEF', 'f': 'ABCDEF'}

# values
['AAA', 'BBB', 'CCC', 'DDD', 'EEE', 'XXX', 'YYY', 'ZZZ']

# setdefault
OOO
{'aaa': 'AAA', 'bbb': 'BBB', 'ccc': 'CCC', 'ddd': 'DDD', 'eee': 'EEE', 'xxx': 'XXX', 'yyy': 'YYY', 'zzz': 'ZZZ', 'ooo': 'OOO'}

# pop
AAA
{'bbb': 'BBB', 'ccc': 'CCC', 'ddd': 'DDD', 'eee': 'EEE', 'xxx': 'XXX', 'yyy': 'YYY', 'zzz': 'ZZZ', 'ooo': 'OOO'}

# popitem
('yyy', 'YYY')
{'bbb': 'BBB', 'ccc': 'CCC', 'ddd': 'DDD', 'eee': 'EEE', 'xxx': 'XXX', 'zzz': 'ZZZ', 'ooo': 'OOO'}

# iterator
0: bbb
1: ccc
2: ddd
3: eee
4: xxx
5: zzz
6: ooo

# iterator iteritems
bbb: BBB
ccc: CCC
ddd: DDD
eee: EEE
xxx: XXX
zzz: ZZZ
ooo: OOO

# iterator iterkeys
0: bbb
1: ccc
2: ddd
3: eee
4: xxx
5: zzz
6: ooo

# iterator itervalues
0: BBB
1: CCC
2: DDD
3: EEE
4: XXX
5: ZZZ
6: OOO

 

Frozenset
<?php
require_once('PHPy.php');

try {
	echo "### PyFronzenset\n\n";
	$fset = new PyFrozenset('abcdef');

	echo "# toString\n";
	echo $fset."\n\n";

	echo "# toArray\n";
	print_r($fset->toArray());
	echo "\n";

	echo "# iterator\n";
	foreach ($fset as $key=>$value) {
		printf("%s: %s\n", $key, $value);
	}
	echo "\n";

	echo "# issubset\n";
	var_dump($fset->issubset('defghi'));
	echo "\n";

	echo "# issuperset\n";
	var_dump($fset->issuperset('defghi'));
	echo "\n";

	echo "# union\n";
	echo $fset->union('defghi')."\n\n";

	echo "# intersection\n";
	echo $fset->intersection('defghi')."\n\n";

	echo "# difference\n";
	echo $fset->difference('defghi')."\n\n";

	echo "# symmetric_difference\n";
	echo $fset->symmetric_difference('defghi')."\n\n";

	echo "# copy\n";
	echo $fset->copy()."\n\n";

} catch (PyException $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());

} catch (Exception $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());
}

 

### PyFronzenset

# toString
PyFrozenset(['a', 'b', 'c', 'd', 'e', 'f'])

# toArray
Array
(
    [0] => a
    [1] => b
    [2] => c
    [3] => d
    [4] => e
    [5] => f
)

# iterator
0: a
1: b
2: c
3: d
4: e
5: f

# issubset
bool(false)

# issuperset
bool(true)

# union
PyFrozenset(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])

# intersection
PyFrozenset(['d', 'e', 'f'])

# difference
PyFrozenset(['a', 'b', 'c'])

# symmetric_difference
PyFrozenset(['a', 'b', 'c', 'g', 'h', 'i'])

# copy
PyFrozenset(['a', 'b', 'c', 'd', 'e', 'f'])

 

Set
<?php
require_once('PHPy.php');

try {
	echo "### PySet\n\n";
	$set = new PySet('abcdef');

	echo "# toString\n";
	echo $set."\n\n";

	echo "# toArray\n";
	print_r($set->toArray());
	echo "\n";

	echo "# iterator\n";
	foreach ($set as $key=>$value) {
		printf("%s: %s\n", $key, $value);
	}
	echo "\n";

	echo "# issubset\n";
	var_dump($set->issubset('defghi'));
	echo "\n";

	echo "# issuperset\n";
	var_dump($set->issuperset('defghi'));
	echo "\n";

	echo "# union\n";
	echo $set->union('defghi')."\n\n";

	echo "# intersection\n";
	echo $set->intersection('defghi')."\n\n";

	echo "# difference\n";
	echo $set->difference('defghi')."\n\n";

	echo "# symmetric_difference\n";
	echo $set->symmetric_difference('defghi')."\n\n";

	echo "# copy\n";
	echo $set->copy()."\n\n";

	echo "# update\n";
	$set->update('defghi');
	echo $set."\n\n";

	echo "# intersection_update\n";
	$set->intersection_update('defghi');
	echo $set."\n\n";

	echo "# difference_update\n";
	$set->difference_update('abcdef');
	echo $set."\n\n";

	echo "# symmetric_difference_update\n";
	$set->symmetric_difference_update('defghi');
	echo $set."\n\n";

	echo "# add\n";
	$set->add('x');
	echo $set."\n\n";

	echo "# remove\n";
	$set->remove('d');
	echo $set."\n\n";

	echo "# discard\n";
	$set->discard('e');
	echo $set."\n\n";

	echo "# pop\n";
	echo $set->pop()."\n";
	echo $set."\n\n";

	echo "# clear\n";
	$set->clear();
	echo $set."\n\n";

} catch (PyException $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());

} catch (Exception $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());
}

 

### PySet

# toString
PySet(['a', 'b', 'c', 'd', 'e', 'f'])

# toArray
Array
(
    [0] => a
    [1] => b
    [2] => c
    [3] => d
    [4] => e
    [5] => f
)

# iterator
0: a
1: b
2: c
3: d
4: e
5: f

# issubset
bool(false)

# issuperset
bool(true)

# union
PySet(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])

# intersection
PySet(['d', 'e', 'f'])

# difference
PySet(['a', 'b', 'c'])

# symmetric_difference
PySet(['a', 'b', 'c', 'g', 'h', 'i'])

# copy
PySet(['a', 'b', 'c', 'd', 'e', 'f'])

# update
PySet(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'])

# intersection_update
PySet(['d', 'e', 'f', 'g', 'h', 'i'])

# difference_update
PySet(['g', 'h', 'i'])

# symmetric_difference_update
PySet(['d', 'e', 'f'])

# add
PySet(['d', 'e', 'f', 'x'])

# remove
PySet(['e', 'f', 'x'])

# discard
PySet(['f', 'x'])

# pop
f
PySet(['x'])

# clear
PySet([])

 

Str
<?php
require_once('PHPy.php');

try {
	echo "### PyStr\n\n";
	$str = new PyStr('abcABCabcABC');

	echo "# slice\n";
	printf("'%s': %s\n", '0:3', $str['0:3']);
	printf("'%s': %s\n", '1:4', $str['1:4']);
	printf("'%s': %s\n", '-3:', $str['-3:']);
	printf("'%s': %s\n", '4', $str['4']);
	echo "\n";

	echo "# toString\n";
	echo $str."\n\n";

	echo "# toArray\n";
	print_r($str->toArray());
	echo "\n";

	echo "# iterator\n";
	foreach ($str as $key=>$value) {
		printf("%s: %s\n", $key, $value);
	}
	echo "\n";

	echo "# capitalize\n";
	echo $str->capitalize()."\n\n";

	echo "# center\n";
	var_dump((string)$str->center(20));
	echo "\n";

	echo "# count\n";
	echo $str->count('abc')."\n\n";

	echo "# endswith\n";
	var_dump($str->endswith("abc"));
	echo "\n";

	echo "# expandtabs\n";
	echo $str->expandtabs()."\n\n";

	echo "# find\n";
	echo $str->find('c')."\n\n";

	echo "# index\n";
	echo $str->index('c')."\n\n";

	echo "# isalnum\n";
	var_dump($str->isalnum());
	echo "\n";

	echo "# isalpha\n";
	var_dump($str->isalpha());
	echo "\n";

	echo "# isdigit\n";
	var_dump($str->isdigit());
	echo "\n";

	echo "# islower\n";
	var_dump($str->islower());
	echo "\n";

	echo "# isspace\n";
	var_dump($str->isspace());
	echo "\n";

	echo "# istitle\n";
	var_dump($str->istitle());
	echo "\n";

	echo "# isupper\n";
	var_dump($str->isupper());
	echo "\n";

	echo "# join\n";
	echo $str->join(new PyList('===', '///', '---'))."\n\n";

	echo "# ljust\n";
	var_dump((string)$str->ljust(20));
	echo "\n";

	echo "# lower\n";
	echo $str->lower()."\n\n";

	echo "# lstrip\n";
	echo $str->lstrip()."\n\n";

	echo "# partition\n";
	echo $str->partition('ABC')."\n\n";

	echo "# replace\n";
	echo $str->replace('ABC', 'OPQ')."\n\n";

	echo "# rfind\n";
	echo $str->rfind('ABC')."\n\n";

	echo "# rindex\n";
	echo $str->rfind('ABC')."\n\n";

	echo "# rjust\n";
	var_dump((string)$str->rjust(20));
	echo "\n";

	echo "# rpartition\n";
	echo $str->rpartition('abc')."\n\n";

	echo "# rsplit\n";
	echo $str->rsplit('c')."\n\n";

	echo "# rstrip\n";
	echo $str->rstrip('C')."\n\n";

	echo "# strip\n";
	echo $str->strip('C')."\n\n";

	echo "# splitlines\n";
	echo $str->splitlines()."\n\n";

	echo "# startswith\n";
	echo $str->startswith('abc')."\n\n";

	echo "# strip\n";
	echo $str->strip('c')."\n\n";

	echo "# swapcase\n";
	echo $str->swapcase()."\n\n";

	echo "# title\n";
	echo $str->title()."\n\n";

	echo "# upper\n";
	echo $str->upper()."\n\n";

	echo "# zfill\n";
	echo $str->zfill(30)."\n\n";

} catch (PyException $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());

} catch (Exception $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());
}

 

### PyStr

# slice
'0:3': abc
'1:4': bcA
'-3:': ABC
'4': B

# toString
abcABCabcABC

# toArray
Array
(
    [0] => a
    [1] => b
    [2] => c
    [3] => A
    [4] => B
    [5] => C
    [6] => a
    [7] => b
    [8] => c
    [9] => A
    [10] => B
    [11] => C
)

# iterator
0: a
1: b
2: c
3: A
4: B
5: C
6: a
7: b
8: c
9: A
10: B
11: C

# capitalize
Abcabcabcabc

# center
string(20) "    abcABCabcABC    "

# count
2

# endswith
bool(false)

# expandtabs
abcABCabcABC

# find
2

# index
2

# isalnum
bool(true)

# isalpha
bool(true)

# isdigit
bool(false)

# islower
bool(false)

# isspace
bool(false)

# istitle
bool(false)

# isupper
bool(false)

# join
===abcABCabcABC///abcABCabcABC---

# ljust
string(20) "abcABCabcABC        "

# lower
abcabcabcabc

# lstrip
abcABCabcABC

# partition
("abc", "ABC", "abcABC")

# replace
abcOPQabcOPQ

# rfind
9

# rindex
9

# rjust
string(20) "        abcABCabcABC"

# rpartition
("abcABC", "abc", "ABC")

# rsplit
['ab', 'ABCab', 'ABC']

# rstrip
abcABCabcAB

# strip
abcABCabcAB

# splitlines
['abcABCabcABC']

# startswith
1

# strip
abcABCabcABC

# swapcase
ABCabcABCabc

# title
Abcabcabcabc

# upper
ABCABCABCABC

# zfill
000000000000000000abcABCabcABC

 

Int
<?php
require_once('PHPy.php');

try {
	echo "### PyInt\n\n";
	$int = new PyInt('FFFFFFFF', 16);

	echo "# toString\n";
	echo $int."\n\n";

	echo "# abs\n";
	echo $int->abs()."\n\n";

	echo "# toInt\n";
	echo $int->toInt()."\n\n";

	echo "# pow\n";
	echo $int->pow(2)."\n\n";

} catch (PyException $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());

} catch (Exception $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());
}

 

### PyInt

# toString
4294967295

# abs
Resource id #6

# toInt
2147483647

# pow
18446744065119617025

 

Xrange
<?php
require_once('PHPy.php');

try {
	echo "### PyXrange\n\n";
	$xrange = new PyXrange(10);

	echo "# toString\n";
	echo $xrange."\n\n";

	echo "# toArray\n";
	print_r($xrange->toArray());
	echo "\n";

	echo "# iterator\n";
	foreach ($xrange as $key=>$value) {
		printf("%s: %s\n", $key, $value);
	}

} catch (PyException $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());

} catch (Exception $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());
}

 

### PyXrange

# toString
xrange(0, 10, 1)

# toArray
Array
(
    [0] => 0
    [1] => 1
    [2] => 2
    [3] => 3
    [4] => 4
    [5] => 5
    [6] => 6
    [7] => 7
    [8] => 8
    [9] => 9
)

# iterator
0: 0
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9

 

Function
<?php
<?php
require_once('PHPy.php');

try {
	echo "### PyFunc\n\n";
	$dict = new PyDict(array('aa'=>'AA', 'bb'=>'BB', 'cc'=>'CC'));

	echo "# PyTuple\n";
	echo PyFunc::PyTuple($dict)."\n\n";

	echo "# PyList\n";
	echo PyFunc::PyList($dict)."\n\n";

	echo "# PyDict\n";
	echo PyFunc::PyDict(array('aA', 'bB', 'cC'))."\n";
	echo PyFunc::PyDict(array('a'=>'AAA', 'b'=>'BBB', 'c'=>'CCC'))."\n";
	echo PyFunc::PyDict(new PyList(new PyTuple('a', 'AAA'), new PyTuple('b', 'BBB'), new PyTuple('c', 'CCC')))."\n";
	echo "\n";

	echo "# PyFrozenset\n";
	echo PyFunc::PyFrozenset('abcdef')."\n\n";

	echo "# PySet\n";
	echo PyFunc::PySet('abcdef')."\n\n";

	echo "# PyStr\n";
	echo PyFunc::PyStr($dict)."\n\n";

	echo "# PyInt\n";
	echo PyFunc::PyInt("FFFFFFFF", 16)."\n\n";

	echo "# PyXrange\n";
	echo PyFunc::PyXrange(0, 20, 2)."\n\n";

	echo "# PyRange\n";
	echo PyFunc::PyRange(0, 20, 2)."\n\n";

	echo "# PyLen\n";
	echo PyFunc::PyLen(new PyTuple('aaa', 'bbb', 'ccc'))."\n\n";

	echo "# PyAnd\n";
	var_dump(PyFunc::PyAnd(1, 2, 3, 0, 4, 5));
	var_dump(PyFunc::PyAnd('a', 'b', 'c', '', 'd'));
	var_dump(PyFunc::PyAnd(array('a', 'b'), array(), array('c')));
	var_dump(PyFunc::PyAnd('a', 'b', 'c'));
	echo "\n";

	echo "# PyOr\n";
	var_dump(PyFunc::PyOr(0, 1, 2, 3));
	var_dump(PyFunc::PyOr('', 'a', 'b', 'c'));
	var_dump(PyFunc::PyOr(array(), array('a', 'b'), array('c')));
	var_dump(PyFunc::PyOr(null, 'a', 'b', 'c'));
	echo "\n\n";

} catch (PyException $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());

} catch (Exception $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());
}

 

### PyFunc

# PyTuple
('aa', 'bb', 'cc')

# PyList
['aa', 'bb', 'cc']

# PyDict
{'a': 'A', 'b': 'B', 'c': 'C'}
{'a': 'AAA', 'b': 'BBB', 'c': 'CCC'}
{'a': 'AAA', 'b': 'BBB', 'c': 'CCC'}

# PyFrozenset
PyFrozenset(['a', 'b', 'c', 'd', 'e', 'f'])

# PySet
PySet(['a', 'b', 'c', 'd', 'e', 'f'])

# PyStr
{'aa': 'AA', 'bb': 'BB', 'cc': 'CC'}

# PyInt
4294967295

# PyXrange
xrange(0, 20, 2)

# PyRange
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

# PyLen
3

# PyAnd
int(0)
string(0) ""
array(0) {
}
string(1) "c"

# PyOr
int(1)
string(1) "a"
array(2) {
  [0]=>
  string(1) "a"
  [1]=>
  string(1) "b"
}
string(1) "a"

 

Other
<?php
require_once('PHPy.php');

try {
	echo "### Other test\n\n";

	echo "# setdefault\n";
	$d = new PyDict();

	$d->setdefault('k1', new PyList())->append('aaa');
	echo $d."\n";

/*
	$d->setdefault('k2', new PyDict())['d1'] = 'bbb';	// Parse error:  syntax error, unexpected '['
	echo $d."\n";
*/

	$d->setdefault('k2', new PyDict())->offsetSet('d1', 'bbb');
	echo $d."\n\n";

} catch (PyException $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());

} catch (Exception $e) {
	printf("%s: %s (Line:%d)\n", get_class($e), $e->getMessage(), $e->getLine());
}

 

### Other test

# setdefault
{'k1': ['aaa']}
{'k1': ['aaa'], 'k2': {'d1': 'bbb'}}

 

PHPy.php

<?php

/* Exception */

class PyException extends Exception{}
class PyTypeError extends PyException{}
class PyValueError extends PyException{}
class PyIndexError extends PyException{}
class PyAttributeError extends PyException{}
class PyKeyError extends PyException{}



/* Function */

class PyFunc
{
	// Cast function

	public static function PyTuple($data=array())
	{
		$data = PyUtils::toArray($data);
		$obj = new PyTuple();
		call_user_func_array(array($obj, '__construct'), $data);
		return $obj;
	}

	public static function PyList($data=array())
	{
		$data = PyUtils::toArray($data);
		$obj = new PyList();
		call_user_func_array(array($obj, '__construct'), $data);
		return $obj;
	}

	public static function PyDict($data=array())
	{
		return new PyDict($data);
	}

	public static function PyFrozenset($data=array())
	{
		return new PyFrozenset($data);
	}

	public static function PySet($data=array())
	{
		return new PySet($data);
	}

	public static function PyStr($obj='')
	{
		return new PyStr($obj);
	}

	public static function PyInt($num=0, $base=10)
	{
		return new PyInt($num, $base);
	}

	public static function PyXrange()
	{
		$args = func_get_args();
		$obj = new PyXrange(1);
		call_user_func_array(array($obj, '__construct'), $args);
		return $obj;
	}


	// Python function

	public static function PyRange()
	{
		$args = func_get_args();
		switch (func_num_args()) {
		case 1:
			$args = array(0, $args[0], 1);
			break;
		case 2:
			$args = array($args[0], $args[1], 1);
			break;
		case 3:
			$args = array($args[0], $args[1], $args[2]);
			break;
		default:
			throw new PyTypeError(sprintf('range expected at least 1 arguments, got %d', func_num_args()));
			break;
		}
		foreach ($args as $arg) {
			if (strcmp((string)(int)$arg, (string)$arg)!=0) {
				throw new PyTypeError('range() integer end argument expected, got str.');
			}
		}
		$range = array();
		if (0<$args[2]) {
			for ($i=$args[0]; $i<$args[1]; $i+=$args[2]) {
				$range[] = $i;
			}
		} elseif ($args[2]<0) {
			for ($i=$args[0]; $args[1]<$i; $i+=$args[2]) {
				$range[] = $i;
			}
		} else {
			throw new PyValueError('range() step argument must not be zero');
		}
		$obj = new PyList();
		call_user_func_array(array($obj, '__construct'), $range);
		return $obj;
	}

	public static function PyLen($obj)
	{
		return PyUtils::getLength($obj);
	}


	// Operator function

	public static function PyAnd()
	{
		$args = func_get_args();
		$obj = null;
		foreach ($args as $obj) {
			if (!PyUtils::toBool($obj)) {
				return $obj;
			}
		}
		return $obj;
	}

	public static function PyOr()
	{
		$args = func_get_args();
		$obj = null;
		foreach ($args as $obj) {
			if (PyUtils::toBool($obj)) {
				return $obj;
			}
		}
		return $obj;
	}
}



/* Utils */

class PyUtils
{
	public static function sliceNormalize($datalen, $slice)
	{
		$slice = str_replace(' ', '', $slice);
		if (!preg_match('/^(\-?[0-9]+)?(:(\-?[0-9]+)?)?$/', $slice) || strlen($slice)==0) {
			throw new PyTypeError('slice indices must be integers');
		}
		$muluti = false;
		$slice = explode(':', $slice);
		$start = (int)$slice[0];
		$end = $start+1;
		// muluti
		if (count($slice)==2) {
			$muluti = true;
			$end = $datalen;
			if (0<strlen($slice[1])) {
				$end = (int)$slice[1];
			}
		}
		// minuse
		if ($start<0) {
			$start = max(0, $start+$datalen);
		}
		if ($end<0) {
			$end = max(0, $end+$datalen);
		}
		// len over
		$start = min($datalen, $start);
		$end = min($datalen, $end);
		return array($start, $end, $muluti);
	}


	/* Cast */

	public static function toArray($obj)
	{
		if ($obj instanceof PyDict) {
			$obj = array_keys($obj->toArray());
		} elseif ($obj instanceof PyObject) {
			$obj = $obj->toArray();
		}
		if (!is_array($obj) && !is_string($obj)) {
			throw new PyTypeError(sprintf("'%s' object is not iterable", gettype($obj)));
		}
		if (!is_array($obj)) {
			$obj = str_split((string)$obj);
		}
		return $obj;
	}

	public static function toAssoc($obj)
	{
		$arr = PyUtils::toArray($obj);
		if ($obj instanceof PyObject) {
			$obj = $obj->toArray();
		}
		// assoc
		if (is_array($obj)) {
			foreach	($obj as $k=>$v) {
				if (!is_int($k)) {
					return $obj;
				}
			}
		}
		// array
		$result = array();
		foreach ($arr as $i=>$item) {
			$item = PyUtils::toPureObject($item);
			try {
				$len = PyUtils::getLength($item);
				if ($len!=2) {
					throw new PyValueError(sprintf('dictionary update sequence element #%d has length %d; 2 is required', $i, $len));
				}
				$result[PyUtils::toString($item[0])] = $item[1];
			} catch (PyTypeError $e) {
				throw new PyTypeError(sprintf('cannot convert dictionary update sequence element #%d to a sequence', $i));
			} catch (PyValueError $e) {
				throw $e;
			}
		}
		return $result;
	}

	public static function toString($obj)
	{
		return (string)$obj;
	}

	public static function toBool($obj)
	{
		if ($obj instanceof PyObject) {
			$obj = $obj->toPureObject();
		}
		if (is_string($obj)) {
			return 0<strlen($obj);
		}
		return (bool)$obj;
	}

	public static function toDump($data, $preSymbol, $sufSymbol, $key_flag, $comma_flag)
	{
		$result = array();
		foreach ($data as $key=>$item) {
			$key = $key_flag ? sprintf("'%s': ", $key) : '';
			// PyObject
			if ($item instanceof PyObject) {
				$result[] = $key . $item->toDump();
			// string
			} elseif (is_string($item)) {
				$item = str_replace('\\', '\\\\', $item);
				$item = str_replace("'", "\\'", $item);
				$result[] = $key . sprintf("'%s'", $item);
			// other
			} else {
				$result[] = $key . json_encode($item);
			}
		}
		$comma = '';
		if (count($result)<=1 && $comma_flag) {
			$comma = ',';
		}
		return $preSymbol . implode(', ', $result) . $comma . $sufSymbol;
	}

	public static function getLength($obj)
	{
		$obj = PyUtils::toPureObject($obj);
		if (is_string($obj)) {
			return strlen($obj);
		} elseif (is_array($obj)) {
			return count($obj);
		}
		throw new PyTypeError(sprintf("object of type '%s' has no len()", gettype($obj)));
	}

	public static function toPureObject($obj)
	{
		if ($obj instanceof PyObject) {
			$obj = $obj->toPureObject();
		}
		return $obj;
	}

	public static function toPureObjectAll($obj)
	{
		if ($obj instanceof PyObject) {
			$obj = $obj->toPureObject();
		}
		$result = $obj;
		if (is_array($obj)) {
			$result = array();
			foreach ($obj as $key=>$value) {
				$result[$key] = PyUtils::toPureObjectAll($value);
			}
		}
		return $result;
	}
}



/* Class */

// Object

abstract class PyObject implements ArrayAccess
{
	public function __toString()
	{
		return $this->toString();
	}

	public function toArray()
	{
		throw new PyTypeError(sprintf("'%s' object is not iterable", get_class($this)));
	}

	abstract public function toString();
	abstract public function toDump();
	abstract public function toPureObject();


	/* ArrayAccess */

	public function offsetExists($offset)
	{
		try {
			$this->offsetGet($offset);
			return true;
		} catch(Exception $e) {
			return false;
		}
	}

	public function offsetUnset($index)
	{
		throw new PyTypeError(sprintf("'%s' object doesn't support item deletion", get_class($this)));
	}

	public function offsetGet($index)
	{
		throw new PyTypeError(sprintf("'%s' object doesn't support item deletion", get_class($this)));
	}

	public function offsetSet($index, $value)
	{
		throw new PyTypeError(sprintf("'%s' object does not support item assignment", get_class($this)));
	}
}


abstract class PyIteratorAggregate extends PyObject implements IteratorAggregate
{
	/* IteratorAggregate */

	public function getIterator()
	{
		if ($this instanceof PyDict) {
			return $this->iterkeys();
		}
		return new ArrayIterator($this->toArray());
	}
}


abstract class PyIterator extends PyObject implements Iterator
{
}


// Tuple

class PyTuple extends PyIteratorAggregate
{
	protected $_data = array();

	public function __construct()
	{
		$this->_data = func_get_args();
	}


	/* Extend method */

	// ArrayAccess

	public function offsetSet($index, $data)
	{
		throw new PyTypeError(sprintf("'%s' object does not support item assignment", get_class($this)));
	}

	public function offsetGet($index)
	{
		list($index_start, $index_end, $muluti) = PyUtils::sliceNormalize(count($this->_data), $index);
		// obj
		if ($muluti) {
			$result = array();
			for ($i=$index_start; $i<$index_end; $i++) {
				if ($i<count($this->_data)) {
					$result[] = $this->_data[$i];
				}
			}
			// instance
			$classname = get_class($this);
			$obj = new $classname();
			call_user_func_array(array($obj, '__construct'), $result);
			return $obj;
		// not obj
		} else {
			if (count($this->_data)<=$index_start) {
				throw new PyIndexError('list index out of range');
			}
			return $this->_data[$index_start];
		}
	}

	// Cast

	public function toArray()
	{
		return $this->_data;
	}

	public function toString()
	{
		return $this->toDump();
	}

	public function toDump()
	{
		return PyUtils::toDump($this->_data, '(', ')', false, true);
	}

	public function toPureObject()
	{
		return $this->toArray();
	}
}


// List

class PyList extends PyTuple
{
	protected $_data = array();

	/* Python method */

	public function append($x)
	{
		array_push($this->_data, $x);
		return $this;
	}

	public function extend($L)
	{
		if ($L instanceof PyObject) {
			$L = $L->toArray();
		}
		if (!is_array($L)) {
			$L = array($L);
		}
		foreach ($L as $x) {
			$this->append($x);
		}
		return $this;
	}

	public function insert($i, $item)
	{
		if (!is_int($i)) {
			throw new PyTypeError('an integer is required');
		}
		while ($i<0) {
			$i += count($this->_data);
		}
		$tmp = array();
		$insert = false;
		foreach ($this->_data as $key=>$value) {
			if ($i==$key) {
				$tmp[] = $item;
				$insert = true;
			}
			$tmp[] = $value;
		}
		if ($insert==false) {
			$tmp[] = $item;
		}
		$this->_data = $tmp;
		return $this;
	}

	public function remove($item)
	{
		$key = array_search($item, $this->_data);
		if ($key===false) {
			throw new PyValueError('list.remove(x): x not in list');
		}
		unset($this->_data[$key]);
		$this->_data = array_values($this->_data);
		return $this;
	}

	public function pop($i=-1)
	{
		if (!is_int($i)) {
			throw new PyTypeError('an integer is required');
		}
		while ($i<0) {
			$i += count($this->_data);
		}
		if (count($this->_data)<=$i) {
			throw new PyIndexError('pop index out of range');
		}
		$pop = $this->_data[$i];
		unset($this->_data[$i]);
		$this->_data = array_values($this->_data);
		return $pop;
	}

	public function index($x)
	{
		$key = array_search($x, $this->_data);
		if ($key==false) {
			throw new PyValueError('list.index(x): x not in list');
		}
		return $key;
	}

	public function count($x)
	{
		$c = 0;
		foreach ($this->_data as $item)
		{
			if ($x===$item) {
				$c++;
			}
		}
		return $c;
	}

	public function sort()
	{
		sort($this->_data);
		$tmp = array();
		// object
		foreach ($this->_data as $item) {
			if (!is_scalar($item)) {
				$tmp[] = $item;
			}
		}
		// scalar
		foreach ($this->_data as $item) {
			if (is_scalar($item)) {
				$tmp[] = $item;
			}
		}
		$this->_data = $tmp;
		return $this;
	}

	public function reverse()
	{
		rsort($this->_data);
		$tmp = array();
		// scalar
		foreach ($this->_data as $item) {
			if (is_scalar($item)) {
				$tmp[] = $item;
			}
		}
		// object
		foreach ($this->_data as $item) {
			if (!is_scalar($item)) {
				$tmp[] = $item;
			}
		}
		$this->_data = $tmp;
		return $this;
	}


	/* Extend method */

	// ArrayAccess

	public function offsetSet($index, $data)
	{
		list($index_start, $index_end, $muluti) = PyUtils::sliceNormalize(count($this->_data), $index);
		// obj
		if ($muluti) {
			// pop
			for ($i=$index_start; $i<$index_end; $i++) {
				if ($index_start<count($this->_data)) {
					$this->pop($index_start);
				}
			}
			// insert
			$data = PyUtils::toArray($data);
			foreach ($data as $key=>$value) {
				$this->insert($index_start+$key, $value);
			}
		// not obj
		} else {
			if (count($this->_data)<=$index_start) {
				throw new PyIndexError('list assignment index out of range');
			}
			$this->_data[$index_start] = $data;
		}
	}

	// Cast

	public function toDump()
	{
		return PyUtils::toDump($this->_data, '[', ']', false, false);
	}
}


// Dict

class PyDict extends PyIteratorAggregate
{
	protected $_data = array();

	public function __construct($arr=array())
	{
		$this->update($arr);
	}

	/* Python method */

	public function clear()
	{
		$this->_data = array();
	}

	public function copy()
	{
		return clone $this;
	}

	public function has_key($k)
	{
		try {
			$this[$k];
		} catch (PyKeyError $e) {
			return false;
		}
		return true;
	}

	public function items()
	{
		$obj = new PyList();
		foreach ($this->_data as $key=> $value) {
			$obj->append(new PyTuple($key, $value));
		}
		return $obj;
	}

	public function keys()
	{
		$keys = array_keys($this->_data);
		$obj = new PyList();
		call_user_func_array(array($obj, '__construct'), $keys);
		return $obj;
	}

	public function update($dict)
	{
		$dict = PyUtils::toAssoc($dict);
		$this->_data = array_merge($this->_data, $dict);
	}

	public static function fromkeys($seq, $value=null)
	{
		$seq = PyUtils::toArray($seq);
		$obj = new PyDict();
		foreach ($seq as $key) {
			$obj[$key] = $value;
		}
		return $obj;
	}

	public function values()
	{
		$values = array_values($this->_data);
		$obj = new PyList();
		call_user_func_array(array($obj, '__construct'), $values);
		return $obj;
	}

	public function get($k, $x=null)
	{
		try {
			$v = $this[$k];
			return $v;
		} catch (PyKeyError $e) {
			return $x;
		}
	}

	public function setdefault($k ,$x=null)
	{
		try {
			return $this[$k];
		} catch (PyKeyError $e) {
			$this[$k] = $x;
			return $this[$k];
		}
	}

	public function pop($k, $x=null)
	{
		try {
			$v = $this[$k];
			unset($this->_data[$k]);
			return $v;
		} catch (PyKeyError $e) {
			return $x;
		}
	}

	public function popitem()
	{
		if (count($this->_data)==0) {
			throw new PyKeyError("'popitem(): dictionary is empty'");
		}
		$key = array_rand($this->_data);
		$value = $this->_data[$key];
		unset($this->_data[$key]);
		return new PyTuple($key, $value);
	}

	public function iteritems()
	{
		return new ArrayIterator($this->toArray());
	}

	public function iterkeys()
	{
		return new ArrayIterator(array_keys($this->toArray()));
	}

	public function itervalues()
	{
		return new ArrayIterator(array_values($this->toArray()));
	}


	/* Extend method */

	// ArrayAccess

	public function offsetGet($key)
	{
		if (!array_key_exists($key, $this->_data)) {
			throw new PyKeyError(sprintf("'%s'", $key));
		}
		return $this->_data[$key];
	}

	public function offsetSet($key, $value)
	{
		$this->_data[$key] = $value;
	}

	// Cast

	public function toArray()
	{
		return $this->_data;
	}

	public function toString()
	{
		return $this->toDump();
	}

	public function toDump()
	{
		return PyUtils::toDump($this->_data, '{', '}', true, false);
	}

	public function toPureObject()
	{
		return $this->toArray();
	}
}


// Frozenset

class PyFrozenset extends PyIteratorAggregate
{
	protected $_data = array();

	public function __construct($data=array())
	{
		$data = PyUtils::toArray($data);
		$data = array_unique($data);
		$this->_data = array_values($data);
	}

	/* Python method*/

	public function issubset($t)
	{
		$t = PyUtils::toArray($t);
		foreach ($t as $item) {
			if (array_search($item, $this->_data)===false) {
				return false;
			}
		}
		return true;
	}

	public function issuperset($t)
	{
		$t = PyUtils::toArray($t);
		foreach ($this->_data as $item) {
			if (array_search($item, $this->_data)===false) {
				return false;
			}
		}
		return true;
	}

	public function union($t)
	{
		$t = PyUtils::toArray($t);
		$union = array_merge($this->_data, $t);
		$classname = get_class($this);
		return new $classname($union);
	}

	public function intersection($t)
	{
		$t = PyUtils::toArray($t);
		$inter = array_intersect($this->_data, $t);
		$classname = get_class($this);
		return new $classname($inter);
	}

	public function difference($t)
	{
		$t = PyUtils::toArray($t);
		$diff = array_diff($this->_data, $t);
		$classname = get_class($this);
		return new $classname($diff);
	}

	public function symmetric_difference($t)
	{
		$t = PyUtils::toArray($t);
		$diff1 = array_diff($this->_data, $t);
		$diff2 = array_diff($t, $this->_data);
		$diff = array_merge($diff1, $diff2);
		$classname = get_class($this);
		return new $classname($diff);
	}

	public function copy()
	{
		return clone $this;
	}


	/* Extend method */

	// Cast

	public function toArray()
	{
		return $this->_data;
	}

	public function toString()
	{
		return $this->toDump();
	}

	public function toDump()
	{
		$classname = get_class($this);
		return PyUtils::toDump($this->_data, $classname.'([', '])', false, false);
	}

	public function toPureObject()
	{
		return $this->toArray();
	}
}


// Set

class PySet extends PyFrozenset
{
	/* Python method */

	public function update($t)
	{
		$t = PyUtils::toArray($t);
		$union = array_merge($this->_data, $t);
		$this->_data = array_unique($union);
	}

	public function intersection_update($t)
	{
		$t = PyUtils::toArray($t);
		$inter = array_intersect($this->_data, $t);
		$this->_data = array_unique($inter);
	}

	public function difference_update($t)
	{
		$t = PyUtils::toArray($t);
		$diff = array_diff($this->_data, $t);
		$this->_data = array_unique($diff);
	}

	public function symmetric_difference_update($t)
	{
		$t = PyUtils::toArray($t);
		$diff1 = array_diff($this->_data, $t);
		$diff2 = array_diff($t, $this->_data);
		$diff = array_merge($diff1, $diff2);
		$this->_data = array_unique($diff);
	}

	public function add($x)
	{
		$this->update(array($x));
	}

	public function remove($x)
	{
		$key = array_search($x, $this->_data);
		if ($key===false) {
			throw new PyKeyError(sprintf("'%s'", $x));
		}
		unset($this->_data[$key]);
	}

	public function discard($x)
	{
		try {
			$this->remove($x);
		} catch (PyKeyError $e) {
		}
	}

	public function pop()
	{
		if (count($this->_data)==0) {
			throw new PyKyError("'pop from an empty set'");
		}
		return array_shift($this->_data);
	}

	public function clear()
	{
		$this->_data = array();
	}
}


// Str

class PyStr extends PyIteratorAggregate
{
	protected $_data = '';

	public function __construct($s='')
	{
		$num = func_num_args();
		if (1<$num) {
			throw new PyTypeError(sprintf("PyStr() takes at most 1 argument (%d given)", $num));
		}
		$this->_data = (string)$s;
	}


	/* Python method */

	public function capitalize()
	{
		return new PyStr(ucfirst(strtolower($this->_data)));
	}

	public function center($width, $fillchar=' ')
	{
		$fillchar = PyUtils::toString($fillchar);
		if (strlen($fillchar)!=1) {
			throw new PyTypeError('center() argument 2 must be char, not str');
		}
		$len = $width - strlen($this->_data);
		if ($len<=0) {
			return $this->_data;
		}
		$l_len = ceil($len/2);
		$r_len = floor($len/2);
		return new PyStr(str_repeat($fillchar, $l_len) . $this->_data . str_repeat($fillchar, $r_len));
	}

	public function count($sub, $start='', $end='')
	{
		$str = (string)$this[sprintf('%s:%s', $start, $end)];
		return substr_count($str, $sub);
	}

	public function endswith($suffix, $start='', $end='')
	{
		if ($suffix=='') {
			return true;
		}
		$str = $this[sprintf('%s:%s', $start, $end)][sprintf('-%s:', strlen($suffix))];
		return $suffix==$str;
	}

	public function expandtabs($tabsize=8)
	{
		$chars = $this->_data;
		if (!is_int($tabsize)) {
			throw new Exception('an integer is required');
		}
		$eol_index = -1;
		for ($i=0; $i<strlen($chars); $i++) {
			if (in_array($chars[$i], array("\r", "\n"))) {
				$eol_index = $i;
			} elseif ($chars[$i]=="\t") {
				$pre_chars = substr($chars, 0, $i);
				$suf_chars = substr($chars, $i+1);
				$space_len = ($tabsize-($i-$eol_index-1)) % $tabsize;
				while ($space_len<=0) {
					$space_len += $tabsize;
				}
				$space = str_repeat(' ', $space_len);
				$chars = $pre_chars.$space.$suf_chars;
			}
		}
		return new PyStr($chars);
	}

	public function find($sub, $start='', $end='')
	{
		list($start, $end) = PyUtils::sliceNormalize(strlen($this->_data), sprintf('%s:%s', $start, $end));
		$str = $this[sprintf('%s:%s', $start, $end)];
		$index = strpos($str, $sub);
		if ($index!==false) {
			return $start+$index;
		}
		return -1;
	}

	public function index($sub, $start='', $end='')
	{
		$index = $this->find($sub, $start, $end);
		if (-1==$index) {
			throw new PyValueError('substring not found');
		}
		return $index;
	}

	public function isalnum()
	{
		return ctype_alnum($this->_data);
	}

	public function isalpha()
	{
		return ctype_alpha($this->_data);
	}

	public function isdigit()
	{
		return ctype_digit($this->_data);
	}

	public function islower()
	{
		return ctype_lower($this->_data);
	}

	public function isspace()
	{
		return ctype_space($this->_data);
	}

	public function istitle()
	{
		return 0<strlen($this->_data) && $this->title()==$this->_data;
	}

	public function isupper()
	{
		return ctype_upper($this->_data);
	}

	public function join($seq)
	{
		$seq = PyUtils::toArray($seq);
		return new PyStr(implode($this->_data, $seq));
	}

	public function ljust($width, $fillchar=' ')
	{
		if ($width instanceof PyInt) {
			$width = $width->toInt();
		}
		if (!is_int($width)) {
			throw new PyTypeError('an integer is required');
		}
		if (strlen($fillchar)!=1) {
			throw new PyTypeError('ljust() argument 2 must be char, not str');
		}
		$data = $this->_data;
		$filllen = $width - strlen($data);
		if (0<$filllen) {
			$data .= str_repeat($fillchar, $filllen);
		}
		return new PyStr($data);
	}

	public function lower()
	{
		return new PyStr(strtolower($this->_data));
	}

	public function lstrip($chars=null)
	{
		if (!is_null($chars)) {
			return ltrim($this->_data, $chars);
		}
		return new PyStr(ltrim($this->_data));
	}

	public function partition($sep)
	{
		$sep = (string)$sep;
		$index = strpos($this->_data, $sep);
		if ($index!==false) {
			$ret1 = substr($this->_data, 0, $index);
			$ret2 = substr($this->_data, $index, strlen($sep));
			$ret3 = substr($this->_data, $index + strlen($sep));
		} else {
			$ret1 = $this->_data;
			$ret2 = '';
			$ret3 = '';
		}
		return new PyTuple(new PyStr($ret1), new PyStr($ret2), new PyStr($ret3));
	}

	public function replace($old, $new, $count=-1)
	{
		$old = (string)$old;
		$new = (string)$new;
		if (!is_int($count)) {
			throw new PyTypeError('an integer is required');
		}
		$result = $this->_data;
		if ($count<0) {
			$result = str_replace($old, $new, $result);
		} else {
			$offset = -1;
			$oldlen = strlen($old);
			$pluslen = strlen($new) - $oldlen;
			for ($i=0; $i<$count; $i++) {
				$index = $offset + 1;
				if (0<$oldlen) {
					$index = strpos($result, $old, max(0, $offset));
				}
				if ($index===false || strlen($result)<$index) {
					break;
				}
				$result = substr($result, 0, $index) . $new . substr($result, $index + $oldlen);
				$offset = max(0, $index + $pluslen);
			}
		}
		return new PyStr($result);
	}

	public function rfind($sub, $start='', $end='')
	{
		list($start, $end) = PyUtils::sliceNormalize(strlen($this->_data), sprintf('%s:%s', $start, $end));
		$str = $this[sprintf('%s:%s', $start, $end)];
		$index = strrpos($str, $sub);
		if ($index!==false) {
			return $start+$index;
		}
		return -1;
	}

	public function rindex($sub, $start='', $end='')
	{
		$index = $this->rfind($sub, $start, $end);
		if (-1==$index) {
			throw new PyValueError('substring not found');
		}
		return $index;
	}

	public function rjust($width, $fillchar=' ')
	{
		if ($width instanceof PyInt) {
			$width = $width->toInt();
		}
		if (!is_int($width)) {
			throw new PyTypeError('an integer is required');
		}
		if (strlen($fillchar)!=1) {
			throw new PyTypeError('rjust() argument 2 must be char, not str');
		}
		$data = $this->_data;
		$filllen = $width - strlen($data);
		if (0<$filllen) {
			$data = str_repeat($fillchar, $filllen) . $data;
		}
		return new PyStr($data);
	}

	public function rpartition($sep)
	{
		$sep = (string)$sep;
		$index = strrpos($this->_data, $sep);
		if ($index!==false) {
			$ret1 = substr($this->_data, 0, $index);
			$ret2 = substr($this->_data, $index, strlen($sep));
			$ret3 = substr($this->_data, $index + strlen($sep));
		} else {
			$ret1 = '';
			$ret2 = '';
			$ret3 = $this->_data;
		}
		return new PyTuple(new PyStr($ret1), new PyStr($ret2), new PyStr($ret3));
	}

	public function rsplit($sep=' ', $maxsplit=-1)
	{
		$sep = (string)$sep;
		if (strlen($sep)==0) {
			throw new PyValueError('empty separator');
		}
		$arr = explode($sep, $this->_data);
		if (-1<$maxsplit) {
			$joincount = count($arr) - $maxsplit;
			for ($i=0; $i<$joincount; $i++) {
				$arr[0] .= $sep . $arr[1];
				unset($arr[1]);
				$arr = array_values($arr);
			}
		}
		$obj = new PyList();
		call_user_func_array(array($obj, '__construct'), $arr);
		return $obj;
	}

	public function rstrip($chars=null)
	{
		if (!is_null($chars)) {
			return rtrim($this->_data, $chars);
		}
		return rtrim($this->_data);
	}

	public function split($sep=' ', $maxsplit=-1)
	{
		$sep = (string)$sep;
		if (strlen($sep)==0) {
			throw new PyValueError('empty separator');
		}
		if (-1<$maxsplit) {
			$arr = explode($sep, $this->_data, $maxsplit);
		} else {
			$arr = explode($sep, $this->_data);
		}
		$obj = new PyList();
		call_user_func_array(array($obj, '__construct'), $arr);
		return $obj;
	}

	public function splitlines($keepends=false)
	{
		$data = $this->_data;
		$arr = array();
		if ($keepends) {
			while (strlen($data) && preg_match("/^[^\r\n]*(\r\n|\r|\n|$)/s", $data, $match)) {
				$match = $match[0];
				$data = substr($data, strlen($match));
				$arr[] = $match;
			}
		} else {
			$data = str_replace("\r\n", "\n", $data);
			$data = str_replace("\r", "\n", $data);
			$arr = explode("\n", $data);
		}
		$obj = new PyList();
		call_user_func_array(array($obj, '__construct'), $arr);
		return $obj;
	}

	public function startswith($prefix, $start='', $end='')
	{
		if ($prefix=='') {
			return true;
		}
		$str = $this[sprintf('%s:%s', $start, $end)][sprintf(':%s', strlen($prefix))];
		return $prefix==$str;
	}

	public function strip($chars=null)
	{
		if (!is_null($chars)) {
			return trim($this->_data, $chars);
		}
		return new PyStr(trim($this->_data));
	}

	public function swapcase()
	{
		$arr = $this->toArray();
		foreach ($arr as &$char) {
			if (preg_match('/[a-z]/', $char)) {
				$char = strtoupper($char);
			} elseif (preg_match('/[A-Z]/', $char)) {
				$char = strtolower($char);
			}
		}
		return new PyStr(implode('', $arr));
	}

	public function title()
	{
		return new PyStr(ucwords(strtolower($this->_data)));
	}

	public function translate($table, $deletechars=null)
	{
		// todo
	}

	public function upper()
	{
		return new PyStr(strtoupper($this->_data));
	}

	public function zfill($width)
	{
		return $this->rjust($width, '0');
	}


	/* Extend method */

	// ArrayAccess

	public function offsetGet($index)
	{
		list($index_start, $index_end, $muluti) = PyUtils::sliceNormalize(strlen($this->_data), $index);
		// obj
		if ($muluti) {
			if ($index_end<$index_start) {
				return new PyStr('');
			}
			return new PyStr(substr($this->_data, $index_start, $index_end-$index_start));
		// not obj
		} else {
			if (strlen($this->_data)<=$index_start) {
				throw new PyIndexError('string index out of range');
			}
			return substr($this->_data, $index_start, 1);
		}
	}

	// Cast

	public function toArray()
	{
		return PyUtils::toArray($this->_data);
	}

	public function toString()
	{
		return $this->_data;
	}

	public function toDump()
	{
		$item = str_replace('\\', '\\\\', $this->_data);
		$item = str_replace('"', '\\"', $item);
		return sprintf('"%s"', $item);
	}

	public function toPureObject()
	{
		return $this->toString();
	}
}


// Int

class PyInt extends PyIteratorAggregate
{
	protected $_res;

	public function __construct($num, $base=10)
	{
		$numorg = $num = PyUtils::toString($num);
		if (!ctype_digit((string)$base)) {
			throw new PyTypeError('an integer is required');
		}
		$base = (int)$base;
		// base
		if ($base==0) {
			if (substr($num, 0, 2)=='0x') {
				$num = '0' . substr($num, 2);
				$base = '16';
			} elseif (substr($num, 0, 1)=='0') {
				$base = 8;
			} else {
				$base = 10;
			}
		}
		// base over
		if ($base<2 || 36<$base) {
			throw new PyValueType('int() base must be >= 2 and <= 36');
		}
		// num
		$chars = '0123456789abcdefghijklmnopqrstuvwxyz';
		$chars = substr($chars, 0, $base);
		if (!preg_match(sprintf('/^[%s]+$/i', $chars), $num)) {
			throw new PyValueError(sprintf("invalid literal for int() with base %d: '%s'", $base, $numorg));
		}
		$this->_res = gmp_init($num, $base);
	}

	public function abs()
	{
		return gmp_abs($this->_res);
	}

	public function toInt()
	{
		return gmp_intval($this->_res);
	}

	public function pow($y)
	{
		if (strcmp((string)(int)$y, (string)$y)!=0) {
			throw new PyTypeError("unsupported operand type(s) for ** or pow(): 'int' and 'str'");
		}
		return gmp_strval(gmp_pow($this->_res, $y));
	}


	/* Extend method */

	// Cast

	public function toString()
	{
		return gmp_strval($this->_res);
	}

	public function toDump()
	{
		return $this->toString();
	}

	public function toPureObject()
	{
		return $this->toString();
	}
}


// Xrange

class PyXrange extends PyIterator
{
	protected $_pos;
	protected $_start;
	protected $_end;
	protected $_step;

	public function __construct()
	{
		$args = func_get_args();
		switch (func_num_args()) {
		case 1:
			$args = array(0, $args[0], 1);
			break;
		case 2:
			$args = array($args[0], $args[1], 1);
			break;
		case 3:
			$args = array($args[0], $args[1], $args[2]);
			break;
		default:
			throw new PyTypeError('xrange() requires 1-3 int arguments');
			break;
		}
		foreach ($args as $arg) {
			if (strcmp((string)(int)$arg, (string)$arg)!=0) {
				throw new PyTypeError('an integer is required');
			}
		}
		if ($args[2]==0) {
			throw new PyValueError('xrange() arg 3 must not be zero');
		}
		$this->_pos = 0;
		$this->_start = (int)$args[0];
		$this->_end = (int)$args[1];
		$this->_step = (int)$args[2];
	}


	/* Extend method */

	// Iterator

	public function rewind()
	{
		$this->_pos = 0;
	}

	public function current()
	{
		return $this->_start + $this->_step * $this->_pos;
	}

	public function key()
	{
		return $this->_pos;
	}

	public function next()
	{
		if (!$this->valid()) {
			throw new PyStopIteration();
		}
		$this->_pos++;
	}

	public function valid()
	{
		$current = $this->current();
		if ($this->_step<0) {
			return $this->_end<$current && $current<=$this->_start;
		}
		return $this->_start<=$current && $current<$this->_end;
	}

	// Cast

	public function toArray()
	{
		return PyFunc::PyRange($this->_start, $this->_end, $this->_step)->toArray();
	}

	public function toString()
	{
		return sprintf('xrange(%d, %d, %d)', $this->_start, $this->_end, $this->_step);
	}

	public function toDump()
	{
		return $this->toString();
	}

	public function toPureObject()
	{
		return $this->toArray();
	}
}