mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 19:40:18 +00:00
This commit is contained in:
parent
cb36966464
commit
721b7a2959
@ -71,7 +71,7 @@ def sorted(iterable, reverse=False):
|
|||||||
|
|
||||||
str.__mul__ = lambda self, n: ''.join([self for _ in range(n)])
|
str.__mul__ = lambda self, n: ''.join([self for _ in range(n)])
|
||||||
|
|
||||||
def str::split(self, sep):
|
def str@split(self, sep):
|
||||||
if sep == "":
|
if sep == "":
|
||||||
return list(self)
|
return list(self)
|
||||||
res = []
|
res = []
|
||||||
@ -86,7 +86,7 @@ def str::split(self, sep):
|
|||||||
res.append(self)
|
res.append(self)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def str::format(self, *args):
|
def str@format(self, *args):
|
||||||
if '{}' in self:
|
if '{}' in self:
|
||||||
for i in range(len(args)):
|
for i in range(len(args)):
|
||||||
self = self.replace('{}', str(args[i]), 1)
|
self = self.replace('{}', str(args[i]), 1)
|
||||||
@ -95,7 +95,7 @@ def str::format(self, *args):
|
|||||||
self = self.replace('{'+str(i)+'}', str(args[i]))
|
self = self.replace('{'+str(i)+'}', str(args[i]))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def str::strip(self, chars=None):
|
def str@strip(self, chars=None):
|
||||||
chars = chars or ' \t\n\r'
|
chars = chars or ' \t\n\r'
|
||||||
i = 0
|
i = 0
|
||||||
while i < len(self) and self[i] in chars:
|
while i < len(self) and self[i] in chars:
|
||||||
@ -127,30 +127,30 @@ def __qsort(a: list, L: int, R: int):
|
|||||||
__qsort(a, L, j)
|
__qsort(a, L, j)
|
||||||
__qsort(a, i, R)
|
__qsort(a, i, R)
|
||||||
|
|
||||||
def list::sort(self, reverse=False):
|
def list@sort(self, reverse=False):
|
||||||
__qsort(self, 0, len(self)-1)
|
__qsort(self, 0, len(self)-1)
|
||||||
if reverse:
|
if reverse:
|
||||||
self.reverse()
|
self.reverse()
|
||||||
|
|
||||||
def list::remove(self, value):
|
def list@remove(self, value):
|
||||||
for i in range(len(self)):
|
for i in range(len(self)):
|
||||||
if self[i] == value:
|
if self[i] == value:
|
||||||
del self[i]
|
del self[i]
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def list::index(self, value):
|
def list@index(self, value):
|
||||||
for i in range(len(self)):
|
for i in range(len(self)):
|
||||||
if self[i] == value:
|
if self[i] == value:
|
||||||
return i
|
return i
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
def list::pop(self, i=-1):
|
def list@pop(self, i=-1):
|
||||||
res = self[i]
|
res = self[i]
|
||||||
del self[i]
|
del self[i]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def list::__eq__(self, other):
|
def list@__eq__(self, other):
|
||||||
if type(self) is not type(other):
|
if type(self) is not type(other):
|
||||||
return False
|
return False
|
||||||
if len(self) != len(other):
|
if len(self) != len(other):
|
||||||
@ -163,7 +163,7 @@ tuple.__eq__ = list.__eq__
|
|||||||
list.__ne__ = lambda self, other: not self.__eq__(other)
|
list.__ne__ = lambda self, other: not self.__eq__(other)
|
||||||
tuple.__ne__ = lambda self, other: not self.__eq__(other)
|
tuple.__ne__ = lambda self, other: not self.__eq__(other)
|
||||||
|
|
||||||
def list::count(self, x):
|
def list@count(self, x):
|
||||||
res = 0
|
res = 0
|
||||||
for i in self:
|
for i in self:
|
||||||
if i == x:
|
if i == x:
|
||||||
@ -171,7 +171,7 @@ def list::count(self, x):
|
|||||||
return res
|
return res
|
||||||
tuple.count = list.count
|
tuple.count = list.count
|
||||||
|
|
||||||
def list::__contains__(self, item):
|
def list@__contains__(self, item):
|
||||||
for i in self:
|
for i in self:
|
||||||
if i == item:
|
if i == item:
|
||||||
return True
|
return True
|
||||||
@ -202,5 +202,5 @@ class staticmethod:
|
|||||||
def __call__(self, *args):
|
def __call__(self, *args):
|
||||||
return self.f(*args)
|
return self.f(*args)
|
||||||
|
|
||||||
def type::__repr__(self):
|
def type@__repr__(self):
|
||||||
return "<class '" + self.__name__ + "'>"
|
return "<class '" + self.__name__ + "'>"
|
59
src/ceval.h
59
src/ceval.h
@ -227,16 +227,12 @@ __NEXT_STEP:;
|
|||||||
STACK_SHRINK(byte.arg);
|
STACK_SHRINK(byte.arg);
|
||||||
PUSH(obj);
|
PUSH(obj);
|
||||||
} DISPATCH();
|
} DISPATCH();
|
||||||
TARGET(BUILD_SLICE) {
|
TARGET(BUILD_SLICE)
|
||||||
_2 = POPX();
|
_2 = POPX(); // step
|
||||||
_1 = POPX();
|
_1 = POPX(); // stop
|
||||||
_0 = POPX();
|
_0 = POPX(); // start
|
||||||
Slice s;
|
PUSH(VAR(Slice(_0, _1, _2)));
|
||||||
if(_0 != None) s.start = CAST(int, _0);
|
DISPATCH();
|
||||||
if(_1 != None) s.stop = CAST(int, _1);
|
|
||||||
if(_2 != None) s.step = CAST(int, _2);
|
|
||||||
PUSH(VAR(s));
|
|
||||||
} DISPATCH();
|
|
||||||
TARGET(BUILD_TUPLE)
|
TARGET(BUILD_TUPLE)
|
||||||
_0 = VAR(STACK_VIEW(byte.arg).to_tuple());
|
_0 = VAR(STACK_VIEW(byte.arg).to_tuple());
|
||||||
STACK_SHRINK(byte.arg);
|
STACK_SHRINK(byte.arg);
|
||||||
@ -352,14 +348,11 @@ __NEXT_STEP:;
|
|||||||
if(asBool(TOP()) == false) frame->jump_abs(byte.arg);
|
if(asBool(TOP()) == false) frame->jump_abs(byte.arg);
|
||||||
else POP();
|
else POP();
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
TARGET(LOOP_CONTINUE) {
|
TARGET(LOOP_CONTINUE)
|
||||||
int target = co_blocks[byte.block].start;
|
frame->jump_abs(co_blocks[byte.block].start);
|
||||||
frame->jump_abs(target);
|
DISPATCH();
|
||||||
} DISPATCH();
|
|
||||||
TARGET(LOOP_BREAK)
|
TARGET(LOOP_BREAK)
|
||||||
frame->jump_abs_break(
|
frame->jump_abs_break(co_blocks[byte.block].end);
|
||||||
co_blocks[byte.block].end
|
|
||||||
);
|
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
TARGET(GOTO) {
|
TARGET(GOTO) {
|
||||||
StrName name(byte.arg);
|
StrName name(byte.arg);
|
||||||
@ -393,11 +386,10 @@ __NEXT_STEP:;
|
|||||||
TARGET(YIELD_VALUE)
|
TARGET(YIELD_VALUE)
|
||||||
return PY_OP_YIELD;
|
return PY_OP_YIELD;
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
TARGET(LIST_APPEND) {
|
TARGET(LIST_APPEND)
|
||||||
PyObject* obj = POPX();
|
_0 = POPX();
|
||||||
List& list = CAST(List&, SECOND());
|
CAST(List&, SECOND()).push_back(_0);
|
||||||
list.push_back(obj);
|
DISPATCH();
|
||||||
} DISPATCH();
|
|
||||||
TARGET(DICT_ADD) {
|
TARGET(DICT_ADD) {
|
||||||
_0 = POPX();
|
_0 = POPX();
|
||||||
Tuple& t = CAST(Tuple&, _0);
|
Tuple& t = CAST(Tuple&, _0);
|
||||||
@ -509,16 +501,15 @@ __NEXT_STEP:;
|
|||||||
PyObject* cls = new_type_object(frame->_module, name, OBJ_GET(Type, super_cls));
|
PyObject* cls = new_type_object(frame->_module, name, OBJ_GET(Type, super_cls));
|
||||||
PUSH(cls);
|
PUSH(cls);
|
||||||
} DISPATCH();
|
} DISPATCH();
|
||||||
TARGET(END_CLASS) {
|
TARGET(END_CLASS)
|
||||||
PyObject* cls = POPX();
|
_0 = POPX();
|
||||||
cls->attr()._try_perfect_rehash();
|
_0->attr()._try_perfect_rehash();
|
||||||
}; DISPATCH();
|
DISPATCH();
|
||||||
TARGET(STORE_CLASS_ATTR) {
|
TARGET(STORE_CLASS_ATTR)
|
||||||
StrName name(byte.arg);
|
_name = StrName(byte.arg);
|
||||||
PyObject* obj = POPX();
|
_0 = POPX();
|
||||||
PyObject* cls = TOP();
|
TOP()->attr().set(_name, _0);
|
||||||
cls->attr().set(name, obj);
|
DISPATCH();
|
||||||
} DISPATCH();
|
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
// // TODO: using "goto" inside with block may cause __exit__ not called
|
// // TODO: using "goto" inside with block may cause __exit__ not called
|
||||||
TARGET(WITH_ENTER)
|
TARGET(WITH_ENTER)
|
||||||
@ -543,8 +534,8 @@ __NEXT_STEP:;
|
|||||||
} DISPATCH();
|
} DISPATCH();
|
||||||
TARGET(EXCEPTION_MATCH) {
|
TARGET(EXCEPTION_MATCH) {
|
||||||
const auto& e = CAST(Exception&, TOP());
|
const auto& e = CAST(Exception&, TOP());
|
||||||
StrName name(byte.arg);
|
_name = StrName(byte.arg);
|
||||||
PUSH(VAR(e.match_type(name)));
|
PUSH(VAR(e.match_type(_name)));
|
||||||
} DISPATCH();
|
} DISPATCH();
|
||||||
TARGET(RAISE) {
|
TARGET(RAISE) {
|
||||||
PyObject* obj = POPX();
|
PyObject* obj = POPX();
|
||||||
|
@ -906,7 +906,7 @@ __SUBSCR_END:
|
|||||||
Str decl_name;
|
Str decl_name;
|
||||||
consume(TK("@id"));
|
consume(TK("@id"));
|
||||||
decl_name = prev().str();
|
decl_name = prev().str();
|
||||||
if(!ctx()->is_compiling_class && match(TK("::"))){
|
if(!ctx()->is_compiling_class && match(TK("@"))){
|
||||||
consume(TK("@id"));
|
consume(TK("@id"));
|
||||||
obj_name = decl_name;
|
obj_name = decl_name;
|
||||||
decl_name = prev().str();
|
decl_name = prev().str();
|
||||||
|
6
src/gc.h
6
src/gc.h
@ -141,6 +141,12 @@ template<> inline void gc_mark<BoundMethod>(BoundMethod& t){
|
|||||||
OBJ_MARK(t.func);
|
OBJ_MARK(t.func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<> inline void gc_mark<Slice>(Slice& t){
|
||||||
|
OBJ_MARK(t.start);
|
||||||
|
OBJ_MARK(t.stop);
|
||||||
|
OBJ_MARK(t.step);
|
||||||
|
}
|
||||||
|
|
||||||
template<> inline void gc_mark<Function>(Function& t){
|
template<> inline void gc_mark<Function>(Function& t){
|
||||||
t.decl->_gc_mark();
|
t.decl->_gc_mark();
|
||||||
if(t._module != nullptr) OBJ_MARK(t._module);
|
if(t._module != nullptr) OBJ_MARK(t._module);
|
||||||
|
@ -19,7 +19,7 @@ constexpr const char* kTokens[] = {
|
|||||||
"&", "&=", "|", "|=", "^", "^=",
|
"&", "&=", "|", "|=", "^", "^=",
|
||||||
"<<", "<<=", ">>", ">>=",
|
"<<", "<<=", ">>", ">>=",
|
||||||
/*****************************************/
|
/*****************************************/
|
||||||
".", ",", ":", ";", "#", "(", ")", "[", "]", "{", "}", "::",
|
".", ",", ":", ";", "#", "(", ")", "[", "]", "{", "}",
|
||||||
"**", "=", ">", "<", "...", "->", "?", "@", "==", "!=", ">=", "<=",
|
"**", "=", ">", "<", "...", "->", "?", "@", "==", "!=", ">=", "<=",
|
||||||
/** KW_BEGIN **/
|
/** KW_BEGIN **/
|
||||||
"class", "import", "as", "def", "lambda", "pass", "del", "from", "with", "yield",
|
"class", "import", "as", "def", "lambda", "pass", "del", "from", "with", "yield",
|
||||||
@ -368,7 +368,7 @@ struct Lexer {
|
|||||||
case '{': add_token(TK("{")); return true;
|
case '{': add_token(TK("{")); return true;
|
||||||
case '}': add_token(TK("}")); return true;
|
case '}': add_token(TK("}")); return true;
|
||||||
case ',': add_token(TK(",")); return true;
|
case ',': add_token(TK(",")); return true;
|
||||||
case ':': add_token_2(':', TK(":"), TK("::")); return true;
|
case ':': add_token(TK(":")); return true;
|
||||||
case ';': add_token(TK(";")); return true;
|
case ';': add_token(TK(";")); return true;
|
||||||
case '(': add_token(TK("(")); return true;
|
case '(': add_token(TK("(")); return true;
|
||||||
case ')': add_token(TK(")")); return true;
|
case ')': add_token(TK(")")); return true;
|
||||||
|
16
src/obj.h
16
src/obj.h
@ -81,19 +81,11 @@ struct Bytes{
|
|||||||
|
|
||||||
using Super = std::pair<PyObject*, Type>;
|
using Super = std::pair<PyObject*, Type>;
|
||||||
|
|
||||||
// TODO: re-examine the design of Slice
|
|
||||||
struct Slice {
|
struct Slice {
|
||||||
int start = 0;
|
PyObject* start;
|
||||||
int stop = 0x7fffffff;
|
PyObject* stop;
|
||||||
int step = 1;
|
PyObject* step;
|
||||||
|
Slice(PyObject* start, PyObject* stop, PyObject* step) : start(start), stop(stop), step(step) {}
|
||||||
void normalize(int len){
|
|
||||||
if(start < 0) start += len;
|
|
||||||
if(stop < 0) stop += len;
|
|
||||||
if(start < 0) start = 0;
|
|
||||||
if(stop > len) stop = len;
|
|
||||||
if(stop < start) stop = start;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class BaseIter {
|
class BaseIter {
|
||||||
|
@ -368,9 +368,10 @@ inline void init_builtins(VM* _vm) {
|
|||||||
const Str& self (CAST(Str&, args[0]));
|
const Str& self (CAST(Str&, args[0]));
|
||||||
|
|
||||||
if(is_type(args[1], vm->tp_slice)){
|
if(is_type(args[1], vm->tp_slice)){
|
||||||
Slice s = _CAST(Slice, args[1]);
|
const Slice& s = _CAST(Slice&, args[1]);
|
||||||
s.normalize(self.u8_length());
|
int start, stop, step;
|
||||||
return VAR(self.u8_slice(s.start, s.stop));
|
vm->parse_int_slice(s, self.u8_length(), start, stop, step);
|
||||||
|
return VAR(self.u8_slice(start, stop, step));
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = CAST(int, args[1]);
|
int index = CAST(int, args[1]);
|
||||||
@ -509,10 +510,11 @@ inline void init_builtins(VM* _vm) {
|
|||||||
const List& self = CAST(List&, args[0]);
|
const List& self = CAST(List&, args[0]);
|
||||||
|
|
||||||
if(is_type(args[1], vm->tp_slice)){
|
if(is_type(args[1], vm->tp_slice)){
|
||||||
Slice s = _CAST(Slice, args[1]);
|
const Slice& s = _CAST(Slice&, args[1]);
|
||||||
s.normalize(self.size());
|
int start, stop, step;
|
||||||
|
vm->parse_int_slice(s, self.size(), start, stop, step);
|
||||||
List new_list;
|
List new_list;
|
||||||
for(size_t i = s.start; i < s.stop; i++) new_list.push_back(self[i]);
|
for(int i=start; step>0?i<stop:i>stop; i+=step) new_list.push_back(self[i]);
|
||||||
return VAR(std::move(new_list));
|
return VAR(std::move(new_list));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,10 +553,11 @@ inline void init_builtins(VM* _vm) {
|
|||||||
const Tuple& self = CAST(Tuple&, args[0]);
|
const Tuple& self = CAST(Tuple&, args[0]);
|
||||||
|
|
||||||
if(is_type(args[1], vm->tp_slice)){
|
if(is_type(args[1], vm->tp_slice)){
|
||||||
Slice s = _CAST(Slice, args[1]);
|
const Slice& s = _CAST(Slice&, args[1]);
|
||||||
s.normalize(self.size());
|
int start, stop, step;
|
||||||
|
vm->parse_int_slice(s, self.size(), start, stop, step);
|
||||||
List new_list;
|
List new_list;
|
||||||
for(size_t i = s.start; i < s.stop; i++) new_list.push_back(self[i]);
|
for(int i=start; step>0?i<stop:i>stop; i+=step) new_list.push_back(self[i]);
|
||||||
return VAR(Tuple(std::move(new_list)));
|
return VAR(Tuple(std::move(new_list)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -877,6 +880,16 @@ inline void VM::post_init(){
|
|||||||
_t(tp_bound_method)->attr().set("__func__", property([](VM* vm, ArgsView args){
|
_t(tp_bound_method)->attr().set("__func__", property([](VM* vm, ArgsView args){
|
||||||
return CAST(BoundMethod&, args[0]).func;
|
return CAST(BoundMethod&, args[0]).func;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
_t(tp_slice)->attr().set("start", property([](VM* vm, ArgsView args){
|
||||||
|
return CAST(Slice&, args[0]).start;
|
||||||
|
}));
|
||||||
|
_t(tp_slice)->attr().set("stop", property([](VM* vm, ArgsView args){
|
||||||
|
return CAST(Slice&, args[0]).stop;
|
||||||
|
}));
|
||||||
|
_t(tp_slice)->attr().set("step", property([](VM* vm, ArgsView args){
|
||||||
|
return CAST(Slice&, args[0]).step;
|
||||||
|
}));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
src/str.h
13
src/str.h
@ -265,11 +265,14 @@ struct Str{
|
|||||||
return substr(i, utf8len(data[i]));
|
return substr(i, utf8len(data[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
Str u8_slice(int start, int end) const{
|
Str u8_slice(int start, int stop, int step) const{
|
||||||
// TODO: optimize this
|
std::stringstream ss;
|
||||||
start = _unicode_index_to_byte(start);
|
if(is_ascii){
|
||||||
end = _unicode_index_to_byte(end);
|
for(int i=start; step>0?i<stop:i>stop; i+=step) ss << data[i];
|
||||||
return substr(start, end - start);
|
}else{
|
||||||
|
for(int i=start; step>0?i<stop:i>stop; i+=step) ss << u8_getitem(i);
|
||||||
|
}
|
||||||
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
int u8_length() const {
|
int u8_length() const {
|
||||||
|
43
src/vm.h
43
src/vm.h
@ -381,6 +381,7 @@ public:
|
|||||||
PyObject* _py_call(PyObject** sp_base, PyObject* callable, ArgsView args, ArgsView kwargs);
|
PyObject* _py_call(PyObject** sp_base, PyObject* callable, ArgsView args, ArgsView kwargs);
|
||||||
PyObject* getattr(PyObject* obj, StrName name, bool throw_err=true);
|
PyObject* getattr(PyObject* obj, StrName name, bool throw_err=true);
|
||||||
PyObject* get_unbound_method(PyObject* obj, StrName name, PyObject** self, bool throw_err=true, bool fallback=false);
|
PyObject* get_unbound_method(PyObject* obj, StrName name, PyObject** self, bool throw_err=true, bool fallback=false);
|
||||||
|
void parse_int_slice(const Slice& s, int length, int& start, int& stop, int& step);
|
||||||
void setattr(PyObject* obj, StrName name, PyObject* value);
|
void setattr(PyObject* obj, StrName name, PyObject* value);
|
||||||
template<int ARGC>
|
template<int ARGC>
|
||||||
void bind_method(PyObject*, Str, NativeFuncC);
|
void bind_method(PyObject*, Str, NativeFuncC);
|
||||||
@ -557,6 +558,48 @@ inline bool VM::asBool(PyObject* obj){
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void VM::parse_int_slice(const Slice& s, int length, int& start, int& stop, int& step){
|
||||||
|
auto clip = [](int value, int min, int max){
|
||||||
|
if(value < min) return min;
|
||||||
|
if(value > max) return max;
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
if(s.step == None) step = 1;
|
||||||
|
else step = CAST(int, s.step);
|
||||||
|
if(step == 0) ValueError("slice step cannot be zero");
|
||||||
|
if(step > 0){
|
||||||
|
if(s.start == None){
|
||||||
|
start = 0;
|
||||||
|
}else{
|
||||||
|
start = CAST(int, s.start);
|
||||||
|
if(start < 0) start += length;
|
||||||
|
start = clip(start, 0, length);
|
||||||
|
}
|
||||||
|
if(s.stop == None){
|
||||||
|
stop = length;
|
||||||
|
}else{
|
||||||
|
stop = CAST(int, s.stop);
|
||||||
|
if(stop < 0) stop += length;
|
||||||
|
stop = clip(stop, 0, length);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if(s.start == None){
|
||||||
|
start = length - 1;
|
||||||
|
}else{
|
||||||
|
start = CAST(int, s.start);
|
||||||
|
if(start < 0) start += length;
|
||||||
|
start = clip(start, -1, length - 1);
|
||||||
|
}
|
||||||
|
if(s.stop == None){
|
||||||
|
stop = -1;
|
||||||
|
}else{
|
||||||
|
stop = CAST(int, s.stop);
|
||||||
|
if(stop < 0) stop += length;
|
||||||
|
stop = clip(stop, -1, length - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline i64 VM::hash(PyObject* obj){
|
inline i64 VM::hash(PyObject* obj){
|
||||||
if (is_non_tagged_type(obj, tp_str)) return CAST(Str&, obj).hash();
|
if (is_non_tagged_type(obj, tp_str)) return CAST(Str&, obj).hash();
|
||||||
if (is_int(obj)) return CAST(i64, obj);
|
if (is_int(obj)) return CAST(i64, obj);
|
||||||
|
@ -81,4 +81,20 @@ assert "Hello, {}!".format("World") == "Hello, World!"
|
|||||||
assert "{} {} {}".format("I", "love", "Python") == "I love Python"
|
assert "{} {} {}".format("I", "love", "Python") == "I love Python"
|
||||||
assert "{0} {1} {2}".format("I", "love", "Python") == "I love Python"
|
assert "{0} {1} {2}".format("I", "love", "Python") == "I love Python"
|
||||||
assert "{2} {1} {0}".format("I", "love", "Python") == "Python love I"
|
assert "{2} {1} {0}".format("I", "love", "Python") == "Python love I"
|
||||||
assert "{0}{1}{0}".format("abra", "cad") == "abracadabra"
|
assert "{0}{1}{0}".format("abra", "cad") == "abracadabra"
|
||||||
|
|
||||||
|
# 3rd slice
|
||||||
|
a = "Hello, World!"
|
||||||
|
assert a[::-1] == "!dlroW ,olleH"
|
||||||
|
assert a[::2] == "Hlo ol!"
|
||||||
|
assert a[2:5:2] == "lo"
|
||||||
|
assert a[5:2:-1] == ",ol"
|
||||||
|
assert a[5:2:-2] == ",l"
|
||||||
|
|
||||||
|
b = list(a)
|
||||||
|
assert b == ['H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!']
|
||||||
|
assert b[::-1] == ['!', 'd', 'l', 'r', 'o', 'W', ' ', ',', 'o', 'l', 'l', 'e', 'H']
|
||||||
|
assert b[::2] == ['H', 'l', 'o', ' ', 'o', 'l', '!']
|
||||||
|
assert b[2:5:2] == ['l', 'o']
|
||||||
|
assert b[5:2:-1] == [',', 'o', 'l']
|
||||||
|
assert b[5:2:-2] == [',', 'l']
|
Loading…
x
Reference in New Issue
Block a user