跳转到内容

模組:PythonScript/Class/Utils

维基百科,自由的百科全书
-- Module:PythonScript/Class/Utils
-- Class 內部輔助工具與內建型別 (str/bytes/number/container) 方法庫
local Utils = {}

local PyClassModel
function Utils.bind_class_model(cls_model)
    PyClassModel = cls_model
end

local function safe_pcall(fn, ...)
    local Runtime = require("Module:PythonScript/Runtime")
    local prev_depth = Runtime.Traceback.eval_depth
    local ok, res = pcall(fn, ...)
    Runtime.Traceback.eval_depth = prev_depth
    return ok, res
end
Utils.safe_pcall = safe_pcall

local function is_none(val)
    return val == nil or (type(val) == "table" and val.__class and val.__class.__name == "NoneType")
end
Utils.is_none = is_none

local function unwrap_int(val)
    if type(val) == "boolean" then return val and 1 or 0 end
    if type(val) == "table" and val.__type == "int" then
        local MathCore = require("Module:PythonScript/Runtime/Cmath")
        return val.value or tonumber(MathCore.BigInt.to_string(val.sign, val.limbs))
    end
    return val
end
Utils.unwrap_int = unwrap_int

local function extract_iterable_elements(iter)
    if iter == nil then return {} end
    if type(iter) == "table" and (iter.__type == "list" or iter.__type == "tuple" or iter.__type == "set" or iter.__type == "frozenset") then
        return iter.elements
    end
    return require("Module:PythonScript/Runtime").builtins.list.__call({iter}).elements
end
Utils.extract_iterable_elements = extract_iterable_elements

local function resolve_search_bounds(total_len, start_arg, end_arg)
    local s = not is_none(start_arg) and unwrap_int(start_arg) or 0
    local e = not is_none(end_arg) and unwrap_int(end_arg) or total_len
    if s < 0 then s = math.max(0, total_len + s) elseif s > total_len then s = total_len end
    if e < 0 then e = math.max(0, total_len + e) elseif e > total_len then e = total_len end
    return s + 1, e
end

function Utils.did_you_mean(target, keys)
    if type(target) ~= "string" then return nil end
    local best, min_d = nil, 999
    for _, k in ipairs(keys) do
        if type(k) == "string" then
            local len1, len2 = #target, #k
            local matrix = {}
            for i = 0, len1 do matrix[i] = {[0] = i} end
            for j = 0, len2 do matrix[0][j] = j end
            for i = 1, len1 do
                for j = 1, len2 do
                    local cost = (target:sub(i, i) == k:sub(j, j) and 0 or 1)
                    matrix[i][j] = math.min(matrix[i-1][j] + 1, matrix[i][j-1] + 1, matrix[i-1][j-1] + cost)
                end
            end
            local d = matrix[len1][len2]
            if d < min_d and d <= math.max(1, math.floor(#target / 2)) then min_d = d; best = k end
        end
    end
    return best
end

Utils.IMPLICIT_DUNDERS = {
    __str__ = true, __repr__ = true, __bool__ = true, __len__ = true,
    __iter__ = true, __next__ = true, __reversed__ = true, __contains__ = true,
    __getitem__ = true, __setitem__ = true, __delitem__ = true,
    __eq__ = true, __ne__ = true, __lt__ = true, __le__ = true, __gt__ = true, __ge__ = true,
    __add__ = true, __sub__ = true, __mul__ = true, __truediv__ = true, __floordiv__ = true,
    __mod__ = true, __pow__ = true, __matmul__ = true, __ldiv__ = true,
    __and__ = true, __or__ = true, __xor__ = true, __lshift__ = true, __rshift__ = true,
    __radd__ = true, __rsub__ = true, __rmul__ = true, __rtruediv__ = true, __rfloordiv__ = true,
    __rmod__ = true, __rpow__ = true, __rmatmul__ = true, __rldiv__ = true,
    __rand__ = true, __ror__ = true, __rxor__ = true, __rlshift__ = true, __rrshift__ = true,
    __neg__ = true, __pos__ = true, __invert__ = true, __abs__ = true, __divmod__ = true,
    __int__ = true, __float__ = true, __bytes__ = true, __fspath__ = true, __hash__ = true,
    __format__ = true, __call__ = true, __enter__ = true, __exit__ = true
}

local function is_eq(a, b, seen)
    if a == b then return true end
    if type(a) == "boolean" and type(b) == "number" then return (a and 1 or 0) == b end
    if type(a) == "number" and type(b) == "boolean" then return a == (b and 1 or 0) end

    if type(a) ~= type(b) then
        if (type(a) == "table" and a.__type == "instance" and a.__builtin_value ~= nil) or (type(b) == "table" and b.__type == "instance" and b.__builtin_value ~= nil) then
            local ua = (type(a) == "table" and a.__type == "instance" and a.__builtin_value ~= nil) and a.__builtin_value or a
            local ub = (type(b) == "table" and b.__type == "instance" and b.__builtin_value ~= nil) and b.__builtin_value or b
            return is_eq(ua, ub, seen)
        end
        local is_big_a = type(a) == "table" and a.__type == "int"
        local is_big_b = type(b) == "table" and b.__type == "int"
        if is_big_a or is_big_b then
            local na = type(a) == "boolean" and (a and 1 or 0) or a
            local nb = type(b) == "boolean" and (b and 1 or 0) or b
            if (type(na) == "number" or is_big_a) and (type(nb) == "number" or is_big_b) then
                local MathCore = require("Module:PythonScript/Runtime/Cmath")
                local sa = is_big_a and na.sign or (na < 0 and -1 or 1)
                local sb = is_big_b and nb.sign or (nb < 0 and -1 or 1)
                if na == 0 then sa = 1 end; if nb == 0 then sb = 1 end
                return sa == sb and MathCore.BigInt.cmp_limbs(MathCore.BigInt.to_limbs(na), MathCore.BigInt.to_limbs(nb)) == 0
            end
        end
        if (type(a) == "table" and a.__type == "complex") or (type(b) == "table" and b.__type == "complex") then
            return require("Module:PythonScript/Runtime/Cmath").Cmath.eq(a, b)
        end
        return false
    end

    if type(a) == "table" then
        local is_b1 = a.__type == "bytes" or a.__type == "bytearray" or a.__type == "memoryview"
        local is_b2 = b.__type == "bytes" or b.__type == "bytearray" or b.__type == "memoryview"
        if is_b1 or is_b2 then return (is_b1 and is_b2) and (a._data == b._data) or false end
        if a.__type == "int" and b.__type == "int" then
            local MathCore = require("Module:PythonScript/Runtime/Cmath")
            return a.sign == b.sign and MathCore.BigInt.cmp_limbs(a.limbs, b.limbs) == 0
        end
        if a.__type == "complex" and b.__type == "complex" then
            return require("Module:PythonScript/Runtime/Cmath").Cmath.eq(a, b)
        end

        seen = seen or {}
        if seen[a] and seen[a][b] then return true end
        seen[a] = seen[a] or {}; seen[a][b] = true

        if (a.__type == "list" and b.__type == "list") or (a.__type == "tuple" and b.__type == "tuple") then
            if #a.elements ~= #b.elements then return false end
            for i = 1, #a.elements do if not is_eq(a.elements[i], b.elements[i], seen) then return false end end
            return true
        end

        local is_s1 = a.__type == "set" or a.__type == "frozenset"
        local is_s2 = b.__type == "set" or b.__type == "frozenset"
        if is_s1 and is_s2 then
            if #a.elements ~= #b.elements then return false end
            for _, ea in ipairs(a.elements) do
                local found = false
                for _, eb in ipairs(b.elements) do if is_eq(ea, eb, seen) then found = true; break end end
                if not found then return false end
            end
            return true
        end

        if a.__type == "dict" and b.__type == "dict" then
            if #a.pairs ~= #b.pairs then return false end
            for _, pa in ipairs(a.pairs) do
                local found = false
                for _, pb in ipairs(b.pairs) do
                    if is_eq(pa.key, pb.key, seen) and is_eq(pa.value, pb.value, seen) then found = true; break end
                end
                if not found then return false end
            end
            return true
        end

        if a.__type == "instance" then
            local ok, eq_m = safe_pcall(PyClassModel.get_attr, a, "__eq__")
            if ok and eq_m and eq_m.__type == "bound_method" then
                local Evaluator = require("Module:PythonScript/Evaluator")
                local Runtime = require("Module:PythonScript/Runtime")
                local res = Evaluator.invoke(eq_m.__func, {b}, {}, eq_m.__self)
                if res ~= Runtime.builtins.NotImplemented then
                    return Runtime.builtins.bool.__call({res})
                end
            end
        end
        if (a.__type == "instance" and a.__builtin_value ~= nil) or (b.__type == "instance" and b.__builtin_value ~= nil) then
            return is_eq(a.__builtin_value ~= nil and a.__builtin_value or a, b.__builtin_value ~= nil and b.__builtin_value or b, seen)
        end
    end
    return a == b
end
Utils.is_eq = is_eq

-- ==========================================
-- 1. 字串與二進位家族方法 (str, bytes, bytearray, memoryview)
-- ==========================================
function Utils.get_str_or_bytes_attr(obj, attr_name)
    local is_str = (type(obj) == "string")
    local is_bytes_family = (type(obj) == "table" and (obj.__type == "bytes" or obj.__type == "bytearray"))
    local is_memview = (type(obj) == "table" and obj.__type == "memoryview")

    if is_str or is_bytes_family then
        local raw_data = is_str and obj or obj._data
        local str_lib = is_bytes_family and string or mw.ustring

        local function wrap_res(res)
            if is_bytes_family and type(res) == "string" then 
                local target_type = obj.__type
                local creator = require("Module:PythonScript/Runtime").builtins[target_type]
                if creator then return creator.__call({res}) end
                return { __type = target_type, _data = res }
            end
            return res
        end

        local function unwrap_arg(val)
            if type(val) == "table" and (val.__type == "bytes" or val.__type == "bytearray" or val.__type == "memoryview") then
                return val._data
            end
            return val
        end

        if attr_name == "lower" then return { __type = "builtin_method", __name = "lower", __call = function() return wrap_res(str_lib.lower(raw_data)) end }
        elseif attr_name == "upper" then return { __type = "builtin_method", __name = "upper", __call = function() return wrap_res(str_lib.upper(raw_data)) end }
        elseif attr_name == "capitalize" then return { __type = "builtin_method", __name = "capitalize", __call = function() return wrap_res(str_lib.upper(str_lib.sub(raw_data, 1, 1)) .. str_lib.lower(str_lib.sub(raw_data, 2))) end }
        elseif attr_name == "title" then return { __type = "builtin_method", __name = "title", __call = function() return wrap_res((str_lib.gsub(raw_data, "(%w)(%w*)", function(first, rest) return str_lib.upper(first) .. str_lib.lower(rest) end))) end }
        elseif attr_name == "strip" or attr_name == "lstrip" or attr_name == "rstrip" then
            return { __type = "builtin_method", __name = attr_name, __call = function(args)
                local chars = args and args[1]
                if is_none(chars) then
                    if attr_name == "strip" then return wrap_res((str_lib.gsub(str_lib.gsub(raw_data, "^%s+", ""), "%s+$", "")))
                    elseif attr_name == "lstrip" then return wrap_res((str_lib.gsub(raw_data, "^%s+", "")))
                    else return wrap_res((str_lib.gsub(raw_data, "%s+$", ""))) end
                end
                local c_str = unwrap_arg(chars)
                if type(c_str) ~= "string" then require("Module:PythonScript/Runtime").raise("TypeError", attr_name .. " arg must be None, str or bytes") end
                local len = str_lib.len(raw_data)
                local s_idx, e_idx = 1, len
                if attr_name == "strip" or attr_name == "lstrip" then
                    while s_idx <= e_idx do
                        local ch = str_lib.sub(raw_data, s_idx, s_idx)
                        if str_lib.find(c_str, ch, 1, true) then s_idx = s_idx + 1 else break end
                    end
                end
                if attr_name == "strip" or attr_name == "rstrip" then
                    while e_idx >= s_idx do
                        local ch = str_lib.sub(raw_data, e_idx, e_idx)
                        if str_lib.find(c_str, ch, 1, true) then e_idx = e_idx - 1 else break end
                    end
                end
                return wrap_res(str_lib.sub(raw_data, s_idx, e_idx))
            end }
        elseif attr_name == "startswith" or attr_name == "endswith" then
            return { __type = "builtin_method", __name = attr_name, __call = function(args)
                local prefix_arg = args[1]
                local total_len = str_lib.len(raw_data)
                local s_pos, e_pos = resolve_search_bounds(total_len, args[2], args[3])
                local target_sub = (s_pos <= e_pos) and str_lib.sub(raw_data, s_pos, e_pos) or ""
                local t_len = str_lib.len(target_sub)
                local candidates = {}
                if type(prefix_arg) == "table" and prefix_arg.__type == "tuple" then
                    for _, el in ipairs(prefix_arg.elements) do table.insert(candidates, unwrap_arg(el)) end
                else table.insert(candidates, unwrap_arg(prefix_arg)) end
                for _, p in ipairs(candidates) do
                    if type(p) ~= "string" then require("Module:PythonScript/Runtime").raise("TypeError", attr_name .. " first arg must be str/bytes or a tuple of str/bytes") end
                    local plen = str_lib.len(p)
                    if plen <= t_len then
                        if attr_name == "startswith" then
                            if str_lib.sub(target_sub, 1, plen) == p then return true end
                        else
                            if plen == 0 or str_lib.sub(target_sub, -plen) == p then return true end
                        end
                    end
                end
                return false
            end }
        elseif attr_name == "find" or attr_name == "index" then
            return { __type = "builtin_method", __name = attr_name, __call = function(args)
                local sub_str = unwrap_arg(args[1])
                if type(sub_str) ~= "string" then require("Module:PythonScript/Runtime").raise("TypeError", "must be str or bytes") end
                local total_len = str_lib.len(raw_data)
                local s_pos, e_pos = resolve_search_bounds(total_len, args[2], args[3])
                local idx = nil
                if s_pos <= e_pos + 1 then
                    local search_area = str_lib.sub(raw_data, 1, e_pos)
                    local found_s, found_e = str_lib.find(search_area, sub_str, s_pos, true)
                    if found_s and found_e <= e_pos then idx = found_s - 1 end
                end
                if idx then return idx end
                if attr_name == "index" then require("Module:PythonScript/Runtime").raise("ValueError", "substring not found") end
                return -1
            end }
        elseif attr_name == "rfind" or attr_name == "rindex" then
            return { __type = "builtin_method", __name = attr_name, __call = function(args)
                local sub_str = unwrap_arg(args[1])
                if type(sub_str) ~= "string" then require("Module:PythonScript/Runtime").raise("TypeError", "must be str or bytes") end
                local total_len = str_lib.len(raw_data)
                local s_pos, e_pos = resolve_search_bounds(total_len, args[2], args[3])
                local last_idx = nil
                if sub_str == "" then
                    if s_pos <= e_pos + 1 then last_idx = e_pos end
                elseif s_pos <= e_pos then
                    local search_area = str_lib.sub(raw_data, 1, e_pos)
                    local cur = s_pos
                    while cur <= e_pos do
                        local ni, nj = str_lib.find(search_area, sub_str, cur, true)
                        if not ni or nj > e_pos then break end
                        last_idx = ni - 1; cur = ni + 1
                    end
                end
                if last_idx then return last_idx end
                if attr_name == "rindex" then require("Module:PythonScript/Runtime").raise("ValueError", "substring not found") end
                return -1
            end }
        elseif attr_name == "count" then
            return { __type = "builtin_method", __name = "count", __call = function(args)
                local sub_str = unwrap_arg(args[1])
                if type(sub_str) ~= "string" then require("Module:PythonScript/Runtime").raise("TypeError", "must be str or bytes") end
                local total_len = str_lib.len(raw_data)
                local s_pos, e_pos = resolve_search_bounds(total_len, args[2], args[3])
                if s_pos > e_pos + 1 then return 0 end
                if sub_str == "" then return math.max(0, e_pos - s_pos + 2) end
                local search_area = str_lib.sub(raw_data, 1, e_pos)
                local c, i = 0, s_pos
                while i <= e_pos do
                    local s_idx, e_idx = str_lib.find(search_area, sub_str, i, true)
                    if s_idx and e_idx <= e_pos then c = c + 1; i = e_idx + 1 else break end
                end
                return c
            end }
        elseif attr_name == "replace" then
            return { __type = "builtin_method", __name = "replace", __call = function(args)
                local old, new, count = unwrap_arg(args[1]), unwrap_arg(args[2]), args[3]
                if type(old) ~= "string" or type(new) ~= "string" then require("Module:PythonScript/Runtime").raise("TypeError", "replace() arguments must be str or bytes") end
                if not is_none(count) then count = unwrap_int(count) else count = nil end
                if count and count == 0 then return wrap_res(raw_data) end
                if old == "" then
                    local chars = {}; local len = str_lib.len(raw_data)
                    local max_rep = (count and count >= 0) and count or (len + 1)
                    local rep_done = 0
                    for i = 1, len do
                        if rep_done < max_rep then table.insert(chars, new); rep_done = rep_done + 1 end
                        table.insert(chars, str_lib.sub(raw_data, i, i))
                    end
                    if rep_done < max_rep then table.insert(chars, new) end
                    return wrap_res(table.concat(chars))
                end
                local safe_old = str_lib.gsub(old, '[%^%$%(%)%%%.%[%]%*%+%-%?]', '%%%1')
                local safe_new = str_lib.gsub(new, '%%', '%%%%')
                local res = (count and count > 0) and str_lib.gsub(raw_data, safe_old, safe_new, count) or str_lib.gsub(raw_data, safe_old, safe_new)
                return wrap_res(res)
            end }
        elseif attr_name == "split" then
            return { __type = "builtin_method", __name = "split", __call = function(args, kwargs)
                local raw_sep = (args and args[1] ~= nil) and args[1] or (kwargs and kwargs.sep)
                local maxsplit = unwrap_int((args and args[2] ~= nil) and args[2] or (kwargs and kwargs.maxsplit) or -1)
                local sep = is_none(raw_sep) and nil or unwrap_arg(raw_sep)
                local res = {}
                if sep == nil then
                    local s = str_lib.gsub(raw_data, "^%s+", "")
                    if s == "" then return { __type = "list", elements = {} } end
                    local splits, pos, len = 0, 1, str_lib.len(s)
                    while pos <= len do
                        if maxsplit >= 0 and splits >= maxsplit then
                            local rest = str_lib.gsub(str_lib.sub(s, pos), "%s+$", "")
                            if rest ~= "" then table.insert(res, wrap_res(rest)) end
                            break
                        end
                        local ws_s, ws_e = str_lib.find(s, "%s+", pos)
                        if not ws_s then table.insert(res, wrap_res(str_lib.sub(s, pos))); break end
                        table.insert(res, wrap_res(str_lib.sub(s, pos, ws_s - 1)))
                        pos = ws_e + 1; splits = splits + 1
                    end
                else
                    if sep == "" then require("Module:PythonScript/Runtime").raise("ValueError", "empty separator") end
                    local start, splits = 1, 0
                    while true do
                        if maxsplit >= 0 and splits >= maxsplit then
                            table.insert(res, wrap_res(str_lib.sub(raw_data, start))); break
                        end
                        local s_idx, e_idx = str_lib.find(raw_data, sep, start, true)
                        if not s_idx then table.insert(res, wrap_res(str_lib.sub(raw_data, start))); break end
                        table.insert(res, wrap_res(str_lib.sub(raw_data, start, s_idx - 1)))
                        start = e_idx + 1; splits = splits + 1
                    end
                end
                return { __type = "list", elements = res }
            end }
        elseif attr_name == "rsplit" then
            return { __type = "builtin_method", __name = "rsplit", __call = function(args, kwargs)
                local raw_sep = (args and args[1] ~= nil) and args[1] or (kwargs and kwargs.sep)
                local maxsplit = unwrap_int((args and args[2] ~= nil) and args[2] or (kwargs and kwargs.maxsplit) or -1)
                if maxsplit < 0 then return Utils.get_str_or_bytes_attr(obj, "split").__call({raw_sep, -1}, {}) end
                local sep = is_none(raw_sep) and nil or unwrap_arg(raw_sep)
                if sep == nil then
                    local s = str_lib.gsub(raw_data, "%s+$", "")
                    if s == "" then return { __type = "list", elements = {} } end
                    local tokens, spans, pos, len = {}, {}, 1, str_lib.len(s)
                    while pos <= len do
                        local ns_s, ns_e = str_lib.find(s, "%S+", pos)
                        if not ns_s then break end
                        table.insert(tokens, str_lib.sub(s, ns_s, ns_e))
                        table.insert(spans, {ns_s, ns_e}); pos = ns_e + 1
                    end
                    if #tokens <= maxsplit + 1 then
                        local res = {}; for _, tk in ipairs(tokens) do table.insert(res, wrap_res(tk)) end
                        return { __type = "list", elements = res }
                    end
                    local cut_idx = #tokens - maxsplit
                    local head_str = str_lib.gsub(str_lib.sub(s, 1, spans[cut_idx][2]), "^%s+", "")
                    local res = { wrap_res(head_str) }
                    for i = cut_idx + 1, #tokens do table.insert(res, wrap_res(tokens[i])) end
                    return { __type = "list", elements = res }
                end
                if sep == "" then require("Module:PythonScript/Runtime").raise("ValueError", "empty separator") end
                local res, cur = {}, str_lib.len(raw_data)
                while maxsplit > 0 and cur >= 0 do
                    local best_i, best_j, s = nil, nil, 1
                    while s <= cur do
                        local ni, nj = str_lib.find(raw_data, sep, s, true)
                        if not ni or ni > cur then break end
                        if nj <= cur then best_i = ni; best_j = nj end
                        s = ni + 1
                    end
                    if best_i then
                        table.insert(res, 1, wrap_res(str_lib.sub(raw_data, best_j + 1, cur)))
                        cur = best_i - 1; maxsplit = maxsplit - 1
                    else break end
                end
                table.insert(res, 1, wrap_res(str_lib.sub(raw_data, 1, cur)))
                return { __type = "list", elements = res }
            end }
        elseif attr_name == "join" then
            return { __type = "builtin_method", __name = "join", __call = function(args)
                local iter_list = extract_iterable_elements(args[1])
                local elts = {}
                for i, v in ipairs(iter_list) do
                    local uv = is_bytes_family and unwrap_arg(v) or v
                    if type(uv) ~= "string" or (not is_bytes_family and type(v) ~= "string") or (is_bytes_family and type(v) == "string") then
                        require("Module:PythonScript/Runtime").raise("TypeError", "sequence item " .. (i - 1) .. ": expected str or bytes-like instance")
                    end
                    table.insert(elts, uv)
                end
                return wrap_res(table.concat(elts, raw_data))
            end }
        elseif attr_name == "isalpha" then return { __type = "builtin_method", __name = "isalpha", __call = function() return str_lib.match(raw_data, "^%a+$") ~= nil end }
        elseif attr_name == "isdigit" then return { __type = "builtin_method", __name = "isdigit", __call = function() return str_lib.match(raw_data, "^%d+$") ~= nil end }
        elseif attr_name == "isalnum" then return { __type = "builtin_method", __name = "isalnum", __call = function() return str_lib.match(raw_data, "^[%w]+$") ~= nil end }
        elseif attr_name == "__format__" and is_str then
            return {
                __type = "builtin_method", __name = "__format__",
                __call = function(args)
                    return require("Module:PythonScript/Runtime").builtins.format({raw_data, args and args[1] or ""})
                end
            }
        elseif attr_name == "format" and is_str then
            return {
                __type = "builtin_method", __name = "format",
                __call = function(args, kwargs)
                    local Runtime = require("Module:PythonScript/Runtime")
                    local auto_idx = 1
                    local L_SEN, R_SEN = "\1__PY_LB__\2", "\1__PY_RB__\2"
                    local escaped = raw_data:gsub("{{", L_SEN):gsub("}}", R_SEN)

                    local function resolve_field(match_str)
                        local field_part, fmt_spec = match_str:match("^([^:]*):(.*)$")
                        if not field_part then field_part = match_str; fmt_spec = "" end
                        local key_expr, conv = field_part:match("^([^!]*)!([rsa])$")
                        if not key_expr then key_expr = field_part end
                        local root_key, rest_path = key_expr:match("^([^%.%[]+)(.*)$")
                        if not root_key then root_key = key_expr; rest_path = "" end

                        local val
                        if root_key == "" then
                            val = args[auto_idx]; auto_idx = auto_idx + 1
                            if val == nil then Runtime.raise("IndexError", "Replacement index out of range") end
                        elseif root_key:match("^%d+$") then
                            val = args[tonumber(root_key) + 1]
                            if val == nil then Runtime.raise("IndexError", "Replacement index " .. root_key .. " out of range") end
                        else
                            val = kwargs and kwargs[root_key]
                            if val == nil then Runtime.raise("KeyError", root_key) end
                        end

                        while rest_path and rest_path ~= "" do
                            local dot_attr, rem1 = rest_path:match("^%.([^%.%[]+)(.*)$")
                            if dot_attr then val = PyClassModel.get_attr(val, dot_attr); rest_path = rem1
                            else
                                local idx_str, rem2 = rest_path:match("^%[(.-)%](.*)$")
                                if idx_str then
                                    if type(val) == "table" and val.__type == "dict" then
                                        local found = false
                                        for _, p in ipairs(val.pairs) do if tostring(p.key) == idx_str then val = p.value; found = true; break end end
                                        if not found then Runtime.raise("KeyError", idx_str) end
                                    elseif type(val) == "table" and val.elements then
                                        val = val.elements[(tonumber(idx_str) or 0) + 1]
                                    end
                                    rest_path = rem2
                                else break end
                            end
                        end

                        if conv == "r" then val = Runtime.builtins.repr({val})
                        elseif conv == "s" then val = Runtime.builtins.str.__call({val})
                        elseif conv == "a" then val = Runtime.builtins.ascii({val}) end

                        return Runtime.builtins.format({val, fmt_spec or ""})
                    end

                    -- 支援巢狀格式規範,如 "{0:{1}}".format("hi", ">5")
                    while escaped:find("{[^{}]*{.-}[^{}]*}") do
                        escaped = str_lib.gsub(escaped, "{([^{}]+)}", resolve_field)
                    end
                    local formatted = str_lib.gsub(escaped, "{(.-)}", resolve_field)
                    return (formatted:gsub(L_SEN, "{"):gsub(R_SEN, "}"))
                end
            }
        elseif attr_name == "__mod__" then
            return {
                __type = "builtin_method",
                __call = function(args)
                    local right = args[1]; local Runtime = require("Module:PythonScript/Runtime")
                    if type(right) == "table" and right.__type == "dict" then
                        return wrap_res((str_lib.gsub(raw_data, "%%%(.-%)[diouxXeEfFgGcrsa]", function(match_str)
                            local key = str_lib.match(match_str, "%%%((.-)%)"); local fmt = str_lib.match(match_str, "[diouxXeEfFgGcrsa]$"); local val = nil
                            for _, p in ipairs(right.pairs) do if unwrap_arg(p.key) == key then val = p.value; break end end
                            if val == nil then Runtime.raise("KeyError", key) end
                            if fmt == "s" or fmt == "r" or fmt == "a" then
                                local fn = (fmt == "r" or fmt == "a") and Runtime.builtins.repr or Runtime.builtins.str.__call
                                local success, s = safe_pcall(fn, {unwrap_arg(val)}); return success and s or tostring(val)
                            else
                                local nval = unwrap_int(val)
                                if type(nval) == "number" then return str_lib.format("%" .. fmt, nval) end
                                local success, s = safe_pcall(Runtime.builtins.str.__call, {unwrap_arg(val)}); return success and s or tostring(val)
                            end
                        end)))
                    end
                    local vals = (type(right) == "table" and right.__type == "tuple") and right.elements or {right}; local v_idx = 1
                    return wrap_res((str_lib.gsub(raw_data, "%%[%-+#0]*[%d%.]*[diouxXeEfFgGcrsa%%]", function(match_str)
                        if match_str == "%%" then return "%" end
                        local val = vals[v_idx]; v_idx = v_idx + 1
                        if val == nil then Runtime.raise("TypeError", "not enough arguments for format string") end
                        local fmt = str_lib.sub(match_str, -1)
                        if fmt == "s" or fmt == "r" or fmt == "a" then
                            local fn = (fmt == "r" or fmt == "a") and Runtime.builtins.repr or Runtime.builtins.str.__call
                            local success, s = safe_pcall(fn, {unwrap_arg(val)}); return success and s or tostring(val)
                        else
                            local nval = unwrap_int(val)
                            if type(nval) == "number" then return str_lib.format(match_str, nval) end
                            local success, s = safe_pcall(Runtime.builtins.str.__call, {unwrap_arg(val)}); return success and s or tostring(val)
                        end
                    end)))
                end
            }
        elseif attr_name == "__iter__" then return { __type = "builtin_method", __call = function() return require("Module:PythonScript/Runtime").builtins.iter({obj}) end }
        elseif attr_name == "casefold" then return { __type = "builtin_method", __call = function() return wrap_res(str_lib.lower(raw_data)) end }
        elseif attr_name == "center" then return { __type = "builtin_method", __call = function(args)
            local w = unwrap_int(args[1] or 0); local fill = unwrap_arg(args[2]) or " "
            local len = str_lib.len(raw_data)
            if len >= w then return wrap_res(raw_data) end
            local pad = w - len; local left = math.floor(pad / 2); local right = pad - left
            return wrap_res(str_lib.rep(fill, left) .. raw_data .. str_lib.rep(fill, right))
        end }
        elseif attr_name == "expandtabs" then return { __type = "builtin_method", __call = function(args)
            local tabsize = unwrap_int(args[1] or 8)
            if tabsize <= 0 then return wrap_res((str_lib.gsub(raw_data, "\t", ""))) end
            return wrap_res((str_lib.gsub(raw_data, "([^\t]*)\t", function(s) return s .. str_lib.rep(" ", tabsize - (str_lib.len(s) % tabsize)) end)))
        end }
        elseif attr_name == "format_map" and is_str then return { __type = "builtin_method", __call = function(args)
            local mapping = args[1]
            return wrap_res((str_lib.gsub(raw_data, "{(.-)}", function(key)
                local Runtime = require("Module:PythonScript/Runtime")
                if type(mapping) == "table" and mapping.__type == "dict" then
                    for _, p in ipairs(mapping.pairs) do
                        if p.key == key then
                            local success, s = safe_pcall(Runtime.builtins.str.__call, {p.value}); return success and s or tostring(p.value)
                        end
                    end
                    Runtime.raise("KeyError", key)
                elseif type(mapping) == "table" and mapping.__type == "instance" then
                    local Evaluator = require("Module:PythonScript/Evaluator")
                    local getitem = PyClassModel.get_attr(mapping, "__getitem__")
                    return Runtime.builtins.str.__call({Evaluator.invoke(getitem, {key}, {})})
                end
                Runtime.raise("TypeError", "format_map() argument must be a mapping")
            end)))
        end }
        elseif attr_name == "isascii" then return { __type = "builtin_method", __call = function() return str_lib.match(raw_data, "^[\0-\127]*$") ~= nil end }
        elseif attr_name == "isdecimal" and is_str then return { __type = "builtin_method", __call = function() return raw_data ~= "" and str_lib.match(raw_data, "^[0-9]+$") ~= nil end }
        elseif attr_name == "isidentifier" and is_str then return { __type = "builtin_method", __call = function() return raw_data ~= "" and str_lib.match(raw_data, "^[%a_][%w_]*$") ~= nil end }
        elseif attr_name == "islower" then return { __type = "builtin_method", __call = function() return str_lib.match(raw_data, "[%a]") ~= nil and raw_data == str_lib.lower(raw_data) end }
        elseif attr_name == "isnumeric" and is_str then return { __type = "builtin_method", __call = function() return raw_data ~= "" and str_lib.match(raw_data, "^[0-9]+$") ~= nil end }
        elseif attr_name == "isprintable" and is_str then return { __type = "builtin_method", __call = function() return not str_lib.find(raw_data, "[%c]") end }
        elseif attr_name == "isspace" then return { __type = "builtin_method", __call = function() return raw_data ~= "" and str_lib.match(raw_data, "^%s+$") ~= nil end }
        elseif attr_name == "istitle" then return { __type = "builtin_method", __call = function()
            if not str_lib.match(raw_data, "[%a]") then return false end
            local expected = str_lib.gsub(raw_data, "(%w)(%w*)", function(first, rest) return str_lib.upper(first) .. str_lib.lower(rest) end)
            return raw_data == expected
        end }
        elseif attr_name == "isupper" then return { __type = "builtin_method", __call = function() return str_lib.match(raw_data, "[%a]") ~= nil and raw_data == str_lib.upper(raw_data) end }
        elseif attr_name == "ljust" then return { __type = "builtin_method", __call = function(args)
            local w = unwrap_int(args[1] or 0); local fill = unwrap_arg(args[2]) or " "
            local len = str_lib.len(raw_data)
            return wrap_res(len >= w and raw_data or raw_data .. str_lib.rep(fill, w - len))
        end }
        elseif attr_name == "rjust" then return { __type = "builtin_method", __call = function(args)
            local w = unwrap_int(args[1] or 0); local fill = unwrap_arg(args[2]) or " "
            local len = str_lib.len(raw_data)
            return wrap_res(len >= w and raw_data or str_lib.rep(fill, w - len) .. raw_data)
        end }
        elseif attr_name == "zfill" then return { __type = "builtin_method", __call = function(args)
            local w = unwrap_int(args[1] or 0); local len = str_lib.len(raw_data)
            if len >= w then return wrap_res(raw_data) end
            local pad = w - len
            if str_lib.match(raw_data, "^[%+%-]") then return wrap_res(str_lib.sub(raw_data, 1, 1) .. str_lib.rep("0", pad) .. str_lib.sub(raw_data, 2)) end
            return wrap_res(str_lib.rep("0", pad) .. raw_data)
        end }
        elseif attr_name == "partition" then return { __type = "builtin_method", __call = function(args)
            local sep = unwrap_arg(args[1]); if not sep or sep == "" then require("Module:PythonScript/Runtime").raise("ValueError", "empty separator") end
            local i, j = str_lib.find(raw_data, sep, 1, true)
            if i then return { __type = "tuple", elements = { wrap_res(str_lib.sub(raw_data, 1, i - 1)), wrap_res(sep), wrap_res(str_lib.sub(raw_data, j + 1)) } }
            else return { __type = "tuple", elements = { wrap_res(raw_data), wrap_res(""), wrap_res("") } } end
        end }
        elseif attr_name == "rpartition" then return { __type = "builtin_method", __call = function(args)
            local sep = unwrap_arg(args[1]); if not sep or sep == "" then require("Module:PythonScript/Runtime").raise("ValueError", "empty separator") end
            local i, j, cur = nil, nil, 1
            while true do
                local ni, nj = str_lib.find(raw_data, sep, cur, true)
                if not ni then break end; i, j = ni, nj; cur = ni + 1
            end
            if i then return { __type = "tuple", elements = { wrap_res(str_lib.sub(raw_data, 1, i - 1)), wrap_res(sep), wrap_res(str_lib.sub(raw_data, j + 1)) } }
            else return { __type = "tuple", elements = { wrap_res(""), wrap_res(""), wrap_res(raw_data) } } end
        end }
        elseif attr_name == "removeprefix" then return { __type = "builtin_method", __call = function(args)
            local p = unwrap_arg(args[1]) or ""; if p == "" then return wrap_res(raw_data) end
            local plen = str_lib.len(p)
            if str_lib.sub(raw_data, 1, plen) == p then return wrap_res(str_lib.sub(raw_data, plen + 1)) else return wrap_res(raw_data) end
        end }
        elseif attr_name == "removesuffix" then return { __type = "builtin_method", __call = function(args)
            local s = unwrap_arg(args[1]) or ""; if s == "" then return wrap_res(raw_data) end
            local slen = str_lib.len(s)
            if str_lib.sub(raw_data, -slen) == s then return wrap_res(str_lib.sub(raw_data, 1, -(slen + 1))) else return wrap_res(raw_data) end
        end }
        elseif attr_name == "splitlines" then return { __type = "builtin_method", __call = function(args, kwargs)
            local keepends = (args and args[1] ~= nil) and args[1] or (kwargs and kwargs.keepends) or false
            keepends = require("Module:PythonScript/Runtime").builtins.bool.__call({keepends})
            local i, len = 1, str_lib.len(raw_data); local list = {}
            while i <= len do
                local rn = str_lib.find(raw_data, "\r\n", i, true); local n = str_lib.find(raw_data, "\n", i, true); local r = str_lib.find(raw_data, "\r", i, true)
                local next_match, match_len, end_char = len + 1, 0, ""
                if rn and rn < next_match then next_match = rn; match_len = 2; end_char = "\r\n" end
                if n and n < next_match then next_match = n; match_len = 1; end_char = "\n" end
                if r and r < next_match then next_match = r; match_len = 1; end_char = "\r" end
                if next_match <= len then
                    local line = str_lib.sub(raw_data, i, next_match - 1)
                    if keepends then line = line .. end_char end
                    table.insert(list, wrap_res(line)); i = next_match + match_len
                else table.insert(list, wrap_res(str_lib.sub(raw_data, i))); break end
            end
            return { __type = "list", elements = list }
        end }
        elseif attr_name == "swapcase" then return { __type = "builtin_method", __call = function()
            return wrap_res((str_lib.gsub(raw_data, ".", function(c)
                local l, u = str_lib.lower(c), str_lib.upper(c)
                if c == l then return u elseif c == u then return l else return c end
            end)))
        end }
        elseif attr_name == "translate" then return { __type = "builtin_method", __call = function(args)
            local table_map = args[1]; local delete_bytes = unwrap_arg(args[2]); local del_set = {}
            if delete_bytes and type(delete_bytes) == "string" then
                for i = 1, #delete_bytes do del_set[string.byte(delete_bytes, i)] = true end
            end
            if is_bytes_family and type(table_map) == "table" and (table_map.__type == "bytes" or table_map.__type == "bytearray") then
                local tdata = table_map._data
                if #tdata ~= 256 then require("Module:PythonScript/Runtime").raise("ValueError", "translation table must be 256 characters long") end
                local out = {}
                for i = 1, #raw_data do
                    local b = string.byte(raw_data, i)
                    if not del_set[b] then table.insert(out, tdata:sub(b + 1, b + 1)) end
                end
                return wrap_res(table.concat(out))
            end
            if type(table_map) ~= "table" or table_map.__type ~= "dict" then require("Module:PythonScript/Runtime").raise("TypeError", "translate() takes a dict or 256-byte table") end
            local lua_map = {}; for _, p in ipairs(table_map.pairs) do lua_map[p.key] = p.value end
            local res = {}
            local function process_cp(cp)
                if del_set[cp] then return end
                local mapped = lua_map[cp]
                if mapped == nil then table.insert(res, str_lib.char(cp))
                elseif is_none(mapped) then
                elseif type(mapped) == "number" then table.insert(res, str_lib.char(mapped))
                else local um = unwrap_arg(mapped); if type(um) == "string" then table.insert(res, um) end end
            end
            if is_bytes_family then for i = 1, str_lib.len(raw_data) do process_cp(str_lib.byte(raw_data, i)) end
            else for cp in mw.ustring.gcodepoint(raw_data) do process_cp(cp) end end
            return wrap_res(table.concat(res))
        end }
        elseif attr_name == "encode" and is_str then return { __type = "builtin_method", __call = function()
            local B = require("Module:PythonScript/Runtime").builtins
            return B.bytes and B.bytes.__call({raw_data}) or { __type = "bytes", _data = raw_data }
        end }
        elseif attr_name == "decode" and is_bytes_family then return { __type = "builtin_method", __call = function() return raw_data end }
        elseif attr_name == "hex" and is_bytes_family then return { __type = "builtin_method", __call = function(args)
            local sep = args and args[1] and unwrap_arg(args[1]) or ""
            local bytes_hex = {}
            for i = 1, #raw_data do table.insert(bytes_hex, string.format("%02x", string.byte(raw_data, i))) end
            return table.concat(bytes_hex, sep)
        end }
        elseif attr_name == "fromhex" and is_bytes_family then
            return require("Module:PythonScript/Runtime").builtins[obj.__type].__dict["fromhex"].__func
        elseif attr_name == "maketrans" then
            if is_bytes_family then
                return { __type = "builtin_method", __call = function(args)
                    local a, b = unwrap_arg(args[1]), unwrap_arg(args[2])
                    if type(a) ~= "string" or type(b) ~= "string" then require("Module:PythonScript/Runtime").raise("TypeError", "maketrans arguments must be bytes-like objects") end
                    if #a ~= #b then require("Module:PythonScript/Runtime").raise("ValueError", "maketrans arguments must have same length") end
                    local tbl = {}; for i = 0, 255 do tbl[i + 1] = string.char(i) end
                    for i = 1, #a do tbl[string.byte(a, i) + 1] = b:sub(i, i) end
                    return { __type = "bytes", _data = table.concat(tbl) }
                end }
            end
            return require("Module:PythonScript/Runtime").builtins.str.__dict["maketrans"].__func
        elseif obj.__type == "bytearray" then
            local Runtime = require("Module:PythonScript/Runtime")
            if attr_name == "append" then return { __type = "builtin_method", __call = function(args)
                local b = unwrap_int(args[1])
                if type(b) ~= "number" or math.floor(b) ~= b then Runtime.raise("TypeError", "an integer is required") end
                if b < 0 or b > 255 then Runtime.raise("ValueError", "byte must be in range(0, 256)") end
                obj._data = obj._data .. string.char(b); return nil
            end }
            elseif attr_name == "extend" then return { __type = "builtin_method", __call = function(args)
                local ext = Runtime.builtins.bytes.__call({args[1]})
                obj._data = obj._data .. ext._data; return nil
            end }
            elseif attr_name == "insert" then return { __type = "builtin_method", __call = function(args)
                local idx, b = unwrap_int(args[1]), unwrap_int(args[2])
                if type(b) ~= "number" or math.floor(b) ~= b then Runtime.raise("TypeError", "an integer is required") end
                if b < 0 or b > 255 then Runtime.raise("ValueError", "byte must be in range(0, 256)") end
                local len = #obj._data
                if idx < 0 then idx = math.max(0, len + idx) elseif idx > len then idx = len end
                obj._data = obj._data:sub(1, idx) .. string.char(b) .. obj._data:sub(idx + 1); return nil
            end }
            elseif attr_name == "pop" then return { __type = "builtin_method", __call = function(args)
                local len = #obj._data
                if len == 0 then Runtime.raise("IndexError", "pop from empty bytearray") end
                local raw_idx = (args and not is_none(args[1])) and unwrap_int(args[1]) or nil
                local idx = raw_idx and (raw_idx < 0 and (len + raw_idx + 1) or (raw_idx + 1)) or len
                if idx < 1 or idx > len then Runtime.raise("IndexError", "pop index out of range") end
                local val = string.byte(obj._data, idx)
                obj._data = obj._data:sub(1, idx - 1) .. obj._data:sub(idx + 1); return val
            end }
            elseif attr_name == "remove" then return { __type = "builtin_method", __call = function(args)
                local b = unwrap_int(args[1])
                if type(b) ~= "number" or math.floor(b) ~= b then Runtime.raise("TypeError", "an integer is required") end
                if b < 0 or b > 255 then Runtime.raise("ValueError", "byte must be in range(0, 256)") end
                local idx = string.find(obj._data, string.char(b), 1, true)
                if not idx then Runtime.raise("ValueError", "value not found in bytearray") end
                obj._data = obj._data:sub(1, idx - 1) .. obj._data:sub(idx + 1); return nil
            end }
            elseif attr_name == "clear" then return { __type = "builtin_method", __call = function() obj._data = ""; return nil end }
            elseif attr_name == "copy" then return { __type = "builtin_method", __call = function() return { __type = "bytearray", _data = obj._data } end }
            elseif attr_name == "reverse" then return { __type = "builtin_method", __call = function() obj._data = string.reverse(obj._data); return nil end }
            end
        end
    elseif is_memview then
        local Runtime = require("Module:PythonScript/Runtime")
        if attr_name == "release" then return { __type = "builtin_method", __call = function() obj._released = true; return nil end } end
        if obj._released then Runtime.raise("ValueError", "operation forbidden on released memoryview object") end
        if attr_name == "tobytes" then return { __type = "builtin_method", __call = function() return { __type = "bytes", _data = obj._data } end }
        elseif attr_name == "tolist" then return { __type = "builtin_method", __call = function()
            local elts = {}; for i = 1, #obj._data do table.insert(elts, string.byte(obj._data, i)) end
            return { __type = "list", elements = elts }
        end }
        elseif attr_name == "hex" then return { __type = "builtin_method", __call = function(args)
            local sep = (args and type(args[1]) == "string") and args[1] or ""
            local bytes_hex = {}; for i = 1, #obj._data do table.insert(bytes_hex, string.format("%02x", string.byte(obj._data, i))) end
            return table.concat(bytes_hex, sep)
        end }
        elseif attr_name == "toreadonly" then return { __type = "builtin_method", __call = function()
            return { __type = "memoryview", _data = obj._data, _obj = obj._obj, _readonly = true, _released = false }
        end }
        elseif attr_name == "obj" then return obj._obj
        elseif attr_name == "nbytes" then return #obj._data
        elseif attr_name == "readonly" then return obj._readonly
        elseif attr_name == "itemsize" then return 1
        elseif attr_name == "ndim" then return 1
        elseif attr_name == "shape" then return { __type = "tuple", elements = { #obj._data } }
        elseif attr_name == "strides" then return { __type = "tuple", elements = { 1 } }
        elseif attr_name == "format" then return "B"
        elseif attr_name == "__iter__" then return { __type = "builtin_method", __call = function() return Runtime.builtins.iter({obj}) end }
        end
    end
    return nil
end

-- ==========================================
-- 2. 數值家族方法 (int, float, bool, complex)
-- ==========================================
function Utils.get_number_attr(obj, attr_name)
    if attr_name == "__format__" then
        return {
            __type = "builtin_method", __name = "__format__",
            __call = function(args)
                return require("Module:PythonScript/Runtime").builtins.format({obj, args and args[1] or ""})
            end
        }
    end
    if type(obj) == "number" or type(obj) == "boolean" or (type(obj) == "table" and obj.__type == "int") then
        local is_integer = (type(obj) == "boolean") or (type(obj) == "number" and math.floor(obj) == obj) or (type(obj) == "table" and obj.__type == "int")
        if is_integer then
            local int_val = (type(obj) == "boolean") and (obj and 1 or 0) or obj
            if attr_name == "real" or attr_name == "numerator" then return int_val end
            if attr_name == "imag" or attr_name == "k" or attr_name == "jk" then return 0 end
            if attr_name == "denominator" then return 1 end
            if attr_name == "conjugate" then return { __type = "builtin_method", __call = function() return int_val end } end
            if attr_name == "is_integer" then return { __type = "builtin_method", __call = function() return true end } end
            if attr_name == "as_integer_ratio" then
                return { __type = "builtin_method", __call = function() return { __type = "tuple", elements = { int_val, 1 } } end }
            end
            if attr_name == "bit_length" then
                return { __type = "builtin_method", __call = function()
                    if int_val == 0 or (type(int_val) == "table" and #int_val.limbs == 1 and int_val.limbs[1] == 0) then return 0 end
                    local bstr = require("Module:PythonScript/Runtime").builtins.bin({int_val}):gsub("^%-?0b", "")
                    return #bstr
                end }
            end
            if attr_name == "bit_count" then
                return { __type = "builtin_method", __call = function()
                    if int_val == 0 or (type(int_val) == "table" and #int_val.limbs == 1 and int_val.limbs[1] == 0) then return 0 end
                    local bstr = require("Module:PythonScript/Runtime").builtins.bin({int_val}):gsub("^%-?0b", "")
                    local _, count = bstr:gsub("1", "")
                    return count
                end }
            end
            if attr_name == "to_bytes" then
                return { __type = "builtin_method", __call = function(args, kwargs)
                    local Runtime = require("Module:PythonScript/Runtime")
                    local length = unwrap_int((args and args[1]) or (kwargs and kwargs.length) or 1)
                    local byteorder = (args and args[2]) or (kwargs and kwargs.byteorder) or "big"
                    local signed = (args and args[3] ~= nil and args[3]) or (kwargs and kwargs.signed) or false
                    local n = unwrap_int(int_val)
                    if n < 0 and not signed then Runtime.raise("OverflowError", "can't convert negative int to unsigned") end
                    if n < 0 then n = (2 ^ (length * 8)) + n end
                    local bytes_arr = {}
                    for i = 1, length do
                        local b = n % 256; n = math.floor(n / 256)
                        if byteorder == "little" then table.insert(bytes_arr, string.char(b))
                        else table.insert(bytes_arr, 1, string.char(b)) end
                    end
                    if n > 0 then Runtime.raise("OverflowError", "int too big to convert") end
                    return { __type = "bytes", _data = table.concat(bytes_arr) }
                end }
            end
            if attr_name == "from_bytes" then
                return { __type = "builtin_method", __call = function(args, kwargs)
                    local Runtime = require("Module:PythonScript/Runtime")
                    local b_obj = args[1]
                    if type(b_obj) == "table" and b_obj.__type == "class" then b_obj = args[2] end
                    local b_data = Runtime.builtins.bytes.__call({b_obj})._data
                    local byteorder = (args and args[2] and type(args[2]) == "string" and args[2]) or (args and args[3]) or (kwargs and kwargs.byteorder) or "big"
                    local res = 0
                    if byteorder == "little" then for i = #b_data, 1, -1 do res = res * 256 + string.byte(b_data, i) end
                    else for i = 1, #b_data do res = res * 256 + string.byte(b_data, i) end end
                    return res
                end }
            end
        end
    end
    if type(obj) == "number" then
        if attr_name == "real" then return obj end
        if attr_name == "imag" or attr_name == "k" or attr_name == "jk" then return 0.0 end
        if attr_name == "conjugate" then return { __type = "builtin_method", __call = function() return obj end } end
        if attr_name == "is_integer" then return { __type = "builtin_method", __call = function() return math.floor(obj) == obj end } end
    elseif type(obj) == "table" and obj.__type == "complex" then
        if attr_name == "real" then return obj.real or 0 end
        if attr_name == "imag" then
            return require("Module:PythonScript/Runtime/Utils").make_quaternion_imag(obj.imag or 0, obj.k or 0, obj.jk or 0)
        end
        if attr_name == "k" then return obj.k or 0 end
        if attr_name == "jk" then return obj.jk or 0 end
        if attr_name == "conjugate" then
            return {
                __type = "builtin_method",
                __call = function()
                    return require("Module:PythonScript/Runtime/Cmath").Cmath.make_complex(
                        obj.real or 0, -(obj.imag or 0), -(obj.k or 0), -(obj.jk or 0)
                    )
                end
            }
        end
    end
    return nil
end

-- ==========================================
-- 3. 容器家族方法 (dict, set, frozenset, list, tuple)
-- ==========================================
function Utils.get_container_attr(obj, attr_name)
    if type(obj) ~= "table" then return nil end

    if obj.__type == "dict" then
        if attr_name == "items" or attr_name == "keys" or attr_name == "values" then
            return { __type = "builtin_method", __name = attr_name, __call = function()
                local function get_view_elements()
                    local res = {}
                    for _, p in ipairs(obj.pairs or {}) do
                        if attr_name == "keys" then table.insert(res, p.key)
                        elseif attr_name == "values" then table.insert(res, p.value)
                        else table.insert(res, {__type="tuple", elements={p.key, p.value}}) end
                    end
                    return res
                end
                local view_methods = {
                    __len__ = { __type = "builtin_method", __call = function() return #(obj.pairs or {}) end },
                    mapping = { __type = "property", fget = { __type = "builtin_method", __call = function()
                        local MappingProxyType = PyClassModel.make_class("MappingProxyType", {}, {
                            __getitem__ = { __type="builtin_method", __call = function(a)
                                for _, p in ipairs(obj.pairs) do if is_eq(p.key, a[2]) then return p.value end end
                                require("Module:PythonScript/Runtime").raise("KeyError", tostring(a[2]))
                            end},
                            __str__ = { __type="builtin_method", __call = function() return "mappingproxy(" .. require("Module:PythonScript/Runtime").builtins.repr({obj}) .. ")" end }
                        })
                        MappingProxyType.__dict.__repr__ = MappingProxyType.__dict.__str__
                        local proxy = PyClassModel.make_instance(MappingProxyType); proxy.__dict._mapping = obj; return proxy
                    end}},
                    __iter__ = { __type = "builtin_method", __call = function()
                        return require("Module:PythonScript/Runtime").builtins.iter({ {__type="list", elements=get_view_elements()} })
                    end},
                    __reversed__ = { __type = "builtin_method", __call = function()
                        local elts = get_view_elements(); local rev = {}
                        for i = #elts, 1, -1 do table.insert(rev, elts[i]) end
                        return require("Module:PythonScript/Runtime").builtins.iter({ {__type="list", elements=rev} })
                    end},
                    __contains__ = { __type = "builtin_method", __call = function(args)
                        for _, e in ipairs(get_view_elements()) do if is_eq(e, args[2]) then return true end end
                        return false
                    end},
                    __repr__ = { __type = "builtin_method", __call = function()
                        local inner = require("Module:PythonScript/Runtime").builtins.repr({ {__type="list", elements=get_view_elements()} })
                        return "dict_" .. attr_name .. "(" .. inner .. ")"
                    end},
                    __eq__ = { __type = "builtin_method", __call = function(args)
                        if attr_name == "values" then return args[1] == args[2] end
                        return is_eq({__type="set", elements=get_view_elements()}, {__type="set", elements=extract_iterable_elements(args[2])})
                    end},
                    __and__ = { __type = "builtin_method", __call = function(args) return Utils.get_container_attr({__type="set", elements=get_view_elements()}, "intersection").__call({args[2]}) end},
                    __or__ = { __type = "builtin_method", __call = function(args) return Utils.get_container_attr({__type="set", elements=get_view_elements()}, "union").__call({args[2]}) end},
                    __sub__ = { __type = "builtin_method", __call = function(args) return Utils.get_container_attr({__type="set", elements=get_view_elements()}, "difference").__call({args[2]}) end},
                    __xor__ = { __type = "builtin_method", __call = function(args) return Utils.get_container_attr({__type="set", elements=get_view_elements()}, "symmetric_difference").__call({args[2]}) end}
                }
                view_methods.__str__ = view_methods.__repr__
                return PyClassModel.make_instance(PyClassModel.make_class("dict_" .. attr_name, {}, view_methods))
            end }
        elseif attr_name == "get" then return { __type = "builtin_method", __name = "get", __call = function(args)
            local k = args[1]; local default = (#args >= 2) and args[2] or require("Module:PythonScript/Runtime").builtins.None
            for _, p in ipairs(obj.pairs) do if is_eq(p.key, k) then return p.value end end
            return default
        end }
        elseif attr_name == "setdefault" then return { __type = "builtin_method", __name = "setdefault", __call = function(args)
            local k = args[1]; local default = (#args >= 2) and args[2] or require("Module:PythonScript/Runtime").builtins.None
            for _, p in ipairs(obj.pairs) do if is_eq(p.key, k) then return p.value end end
            table.insert(obj.pairs, {key = k, value = default})
            if obj.__scope_ref and type(k) == "string" then obj.__scope_ref[k] = default end
            return default
        end }
        elseif attr_name == "pop" then return { __type = "builtin_method", __name = "pop", __call = function(args)
            local k = args[1]
            for i, p in ipairs(obj.pairs) do
                if is_eq(p.key, k) then
                    table.remove(obj.pairs, i)
                    if obj.__scope_ref and type(k) == "string" then obj.__scope_ref[k] = nil end
                    return p.value
                end
            end
            if #args >= 2 then return args[2] end
            require("Module:PythonScript/Runtime").raise("KeyError", tostring(k))
        end }
        elseif attr_name == "popitem" then return { __type = "builtin_method", __name = "popitem", __call = function()
            if #obj.pairs == 0 then require("Module:PythonScript/Runtime").raise("KeyError", "'popitem(): dictionary is empty'") end
            local p = table.remove(obj.pairs)
            if obj.__scope_ref and type(p.key) == "string" then obj.__scope_ref[p.key] = nil end
            return { __type = "tuple", elements = { p.key, p.value } }
        end }
        elseif attr_name == "clear" then return { __type = "builtin_method", __name = "clear", __call = function()
            obj.pairs = {}; if obj.__scope_ref then for k in pairs(obj.__scope_ref) do obj.__scope_ref[k] = nil end end; return nil
        end }
        elseif attr_name == "copy" then return { __type = "builtin_method", __name = "copy", __call = function()
            local res = {__type="dict", pairs={}}; for _, p in ipairs(obj.pairs) do table.insert(res.pairs, {key=p.key, value=p.value}) end; return res
        end }
        elseif attr_name == "update" then return { __type = "builtin_method", __name = "update", __call = function(args, kwargs)
            local other = args and args[1]
            local function put_kv(k, v)
                local found = false
                for _, p in ipairs(obj.pairs) do if is_eq(p.key, k) then p.value = v; found = true; break end end
                if not found then table.insert(obj.pairs, {key=k, value=v}) end
                if obj.__scope_ref and type(k) == "string" then obj.__scope_ref[k] = v end
            end
            if other ~= nil and not is_none(other) then
                if type(other) == "table" and other.__type == "dict" then
                    for _, op in ipairs(other.pairs) do put_kv(op.key, op.value) end
                else
                    for _, item in ipairs(extract_iterable_elements(other)) do
                        if type(item) == "table" and (item.__type == "list" or item.__type == "tuple") and #item.elements == 2 then
                            put_kv(item.elements[1], item.elements[2])
                        else require("Module:PythonScript/Runtime").raise("ValueError", "dictionary update sequence element has length ~= 2") end
                    end
                end
            end
            if kwargs then for k, v in pairs(kwargs) do put_kv(k, v) end end
            return nil
        end }
        elseif attr_name == "fromkeys" then return { __type = "builtin_method", __name = "fromkeys", __call = function(args)
            local iter_arg, val_arg = args[1], args[2]
            if type(iter_arg) == "table" and iter_arg.__type == "class" then iter_arg = args[2]; val_arg = args[3] end
            if val_arg == nil then val_arg = require("Module:PythonScript/Runtime").builtins.None end
            local res = { __type = "dict", pairs = {} }
            for _, k in ipairs(extract_iterable_elements(iter_arg)) do
                local found = false
                for _, p in ipairs(res.pairs) do if is_eq(p.key, k) then p.value = val_arg; found = true; break end end
                if not found then table.insert(res.pairs, {key = k, value = val_arg}) end
            end
            return res
        end }
        elseif attr_name == "__iter__" then return { __type = "builtin_method", __call = function() return require("Module:PythonScript/Runtime").builtins.iter({obj}) end }
        elseif attr_name == "__len__" then return { __type = "builtin_method", __name = "__len__", __call = function() return #(obj.pairs or {}) end }
        elseif attr_name == "__contains__" then return { __type = "builtin_method", __name = "__contains__", __call = function(args)
            local k = args[1]
            for _, p in ipairs(obj.pairs) do if is_eq(p.key, k) then return true end end
            return false
        end }
        elseif attr_name == "__getitem__" then return { __type = "builtin_method", __name = "__getitem__", __call = function(args)
            local k = args[1]
            for _, p in ipairs(obj.pairs) do if is_eq(p.key, k) then return p.value end end
            require("Module:PythonScript/Runtime").raise("KeyError", tostring(k))
        end }
        elseif attr_name == "__setitem__" then return { __type = "builtin_method", __name = "__setitem__", __call = function(args)
            local k, v = args[1], args[2]
            local found = false
            for _, p in ipairs(obj.pairs) do if is_eq(p.key, k) then p.value = v; found = true; break end end
            if not found then table.insert(obj.pairs, {key = k, value = v}) end
            if obj.__scope_ref and type(k) == "string" then obj.__scope_ref[k] = v end
            return require("Module:PythonScript/Runtime").builtins.None
        end }
        elseif attr_name == "__delitem__" then return { __type = "builtin_method", __name = "__delitem__", __call = function(args)
            local k = args[1]
            for i, p in ipairs(obj.pairs) do
                if is_eq(p.key, k) then
                    table.remove(obj.pairs, i)
                    if obj.__scope_ref and type(k) == "string" then obj.__scope_ref[k] = nil end
                    return require("Module:PythonScript/Runtime").builtins.None
                end
            end
            require("Module:PythonScript/Runtime").raise("KeyError", tostring(k))
        end }
        end

    elseif obj.__type == "set" or obj.__type == "frozenset" then
        local is_mutable_set = (obj.__type == "set")
        local set_type = obj.__type
        if is_mutable_set and attr_name == "add" then return { __type = "builtin_method", __name = "add", __call = function(args)
            local val = args[1]; local exists = false
            for _, e in ipairs(obj.elements) do if is_eq(e, val) then exists = true; break end end
            if not exists then table.insert(obj.elements, val) end; return nil
        end }
        elseif is_mutable_set and attr_name == "remove" then return { __type = "builtin_method", __name = "remove", __call = function(args)
            local val = args[1]
            for i, e in ipairs(obj.elements) do if is_eq(e, val) then table.remove(obj.elements, i); return nil end end
            require("Module:PythonScript/Runtime").raise("KeyError", tostring(val))
        end }
        elseif is_mutable_set and attr_name == "discard" then return { __type = "builtin_method", __name = "discard", __call = function(args)
            local val = args[1]
            for i, e in ipairs(obj.elements) do if is_eq(e, val) then table.remove(obj.elements, i); break end end; return nil
        end }
        elseif is_mutable_set and attr_name == "pop" then return { __type = "builtin_method", __name = "pop", __call = function()
            if #obj.elements == 0 then require("Module:PythonScript/Runtime").raise("KeyError", "'pop from an empty set'") end
            return table.remove(obj.elements, 1)
        end }
        elseif is_mutable_set and attr_name == "clear" then return { __type = "builtin_method", __name = "clear", __call = function() obj.elements = {}; return nil end }
        elseif attr_name == "copy" then return { __type = "builtin_method", __name = "copy", __call = function()
            local res = {__type = set_type, elements = {}}; for _, v in ipairs(obj.elements) do table.insert(res.elements, v) end; return res
        end }
        elseif attr_name == "union" then return { __type = "builtin_method", __name = "union", __call = function(args)
            local res = {__type = set_type, elements = {}}
            for _, v in ipairs(obj.elements) do table.insert(res.elements, v) end
            for _, other in ipairs(args or {}) do
                for _, v in ipairs(extract_iterable_elements(other)) do
                    local exists = false
                    for _, e in ipairs(res.elements) do if is_eq(e, v) then exists = true; break end end
                    if not exists then table.insert(res.elements, v) end
                end
            end
            return res
        end }
        elseif attr_name == "intersection" then return { __type = "builtin_method", __name = "intersection", __call = function(args)
            local curr = {}; for _, v in ipairs(obj.elements) do table.insert(curr, v) end
            for _, other in ipairs(args or {}) do
                local other_elts = extract_iterable_elements(other); local next_curr = {}
                for _, v in ipairs(curr) do
                    for _, v2 in ipairs(other_elts) do if is_eq(v, v2) then table.insert(next_curr, v); break end end
                end
                curr = next_curr
            end
            return {__type = set_type, elements = curr}
        end }
        elseif attr_name == "difference" then return { __type = "builtin_method", __name = "difference", __call = function(args)
            local curr = {}; for _, v in ipairs(obj.elements) do table.insert(curr, v) end
            for _, other in ipairs(args or {}) do
                local other_elts = extract_iterable_elements(other); local next_curr = {}
                for _, v in ipairs(curr) do
                    local found = false
                    for _, v2 in ipairs(other_elts) do if is_eq(v, v2) then found = true; break end end
                    if not found then table.insert(next_curr, v) end
                end
                curr = next_curr
            end
            return {__type = set_type, elements = curr}
        end }
        elseif attr_name == "symmetric_difference" then return { __type = "builtin_method", __name = "symmetric_difference", __call = function(args)
            local other_elts = extract_iterable_elements(args[1]); local res = {__type = set_type, elements = {}}
            for _, v in ipairs(obj.elements) do
                local found = false
                for _, v2 in ipairs(other_elts) do if is_eq(v, v2) then found = true; break end end
                if not found then table.insert(res.elements, v) end
            end
            for _, v2 in ipairs(other_elts) do
                local found = false
                for _, v in ipairs(obj.elements) do if is_eq(v, v2) then found = true; break end end
                if not found then
                    local dup = false
                    for _, e in ipairs(res.elements) do if is_eq(e, v2) then dup = true; break end end
                    if not dup then table.insert(res.elements, v2) end
                end
            end
            return res
        end }
        elseif is_mutable_set and attr_name == "update" then return { __type = "builtin_method", __name = "update", __call = function(args)
            obj.elements = Utils.get_container_attr(obj, "union").__call(args).elements; return nil
        end }
        elseif is_mutable_set and attr_name == "intersection_update" then return { __type = "builtin_method", __name = "intersection_update", __call = function(args)
            obj.elements = Utils.get_container_attr(obj, "intersection").__call(args).elements; return nil
        end }
        elseif is_mutable_set and attr_name == "difference_update" then return { __type = "builtin_method", __name = "difference_update", __call = function(args)
            obj.elements = Utils.get_container_attr(obj, "difference").__call(args).elements; return nil
        end }
        elseif is_mutable_set and attr_name == "symmetric_difference_update" then return { __type = "builtin_method", __name = "symmetric_difference_update", __call = function(args)
            obj.elements = Utils.get_container_attr(obj, "symmetric_difference").__call(args).elements; return nil
        end }
        elseif attr_name == "issubset" then return { __type = "builtin_method", __name = "issubset", __call = function(args)
            local other_elts = extract_iterable_elements(args[1])
            for _, v in ipairs(obj.elements) do
                local found = false
                for _, v2 in ipairs(other_elts) do if is_eq(v, v2) then found = true; break end end
                if not found then return false end
            end
            return true
        end }
        elseif attr_name == "issuperset" then return { __type = "builtin_method", __name = "issuperset", __call = function(args)
            local other_elts = extract_iterable_elements(args[1])
            for _, v2 in ipairs(other_elts) do
                local found = false
                for _, v in ipairs(obj.elements) do if is_eq(v, v2) then found = true; break end end
                if not found then return false end
            end
            return true
        end }
        elseif attr_name == "isdisjoint" then return { __type = "builtin_method", __name = "isdisjoint", __call = function(args)
            local other_elts = extract_iterable_elements(args[1])
            for _, v in ipairs(obj.elements) do
                for _, v2 in ipairs(other_elts) do if is_eq(v, v2) then return false end end
            end
            return true
        end }
        elseif attr_name == "__iter__" then return { __type = "builtin_method", __call = function() return require("Module:PythonScript/Runtime").builtins.iter({obj}) end }
        end

    elseif obj.__type == "list" or obj.__type == "tuple" then
        if obj.is_matrix then
            if attr_name == "shape" then
                local s = obj.shape or require("Module:PythonScript/Library").Matrix.get_shape(obj) or {}
                return { __type = "tuple", elements = s }
            elseif attr_name == "T" then return require("Module:PythonScript/Library").Matrix.transpose(obj)
            elseif attr_name == "I" then return require("Module:PythonScript/Library").Matrix.pinv(obj)
            elseif attr_name == "inv" then return { __type = "builtin_method", __call = function() return require("Module:PythonScript/Library").Matrix.inv(obj) end }
            elseif attr_name == "pinv" then return { __type = "builtin_method", __call = function() return require("Module:PythonScript/Library").Matrix.pinv(obj) end }
            elseif attr_name == "det" then return { __type = "builtin_method", __call = function() return require("Module:PythonScript/Library").Matrix.det(obj) end }
            elseif attr_name == "trace" then return { __type = "builtin_method", __call = function() return require("Module:PythonScript/Library").Matrix.trace(obj) end }
            elseif attr_name == "rank" then return { __type = "builtin_method", __call = function() return require("Module:PythonScript/Library").Matrix.rank(obj) end }
            end
        end
        if attr_name == "append" and obj.__type == "list" then return { __type = "builtin_method", __name = "append", __call = function(args) table.insert(obj.elements, args[1]); return nil end }
        elseif attr_name == "extend" and obj.__type == "list" then return { __type = "builtin_method", __name = "extend", __call = function(args)
            for _, v in ipairs(extract_iterable_elements(args[1])) do table.insert(obj.elements, v) end; return nil
        end }
        elseif attr_name == "insert" and obj.__type == "list" then return { __type = "builtin_method", __name = "insert", __call = function(args)
            local raw_idx, val = unwrap_int(args[1]), args[2]; local len = #obj.elements
            if raw_idx < 0 then raw_idx = len + raw_idx end
            if raw_idx < 0 then raw_idx = 0 elseif raw_idx > len then raw_idx = len end
            table.insert(obj.elements, raw_idx + 1, val); return nil
        end }
        elseif attr_name == "pop" and obj.__type == "list" then return { __type = "builtin_method", __name = "pop", __call = function(args)
            local len = #obj.elements
            if len == 0 then require("Module:PythonScript/Runtime").raise("IndexError", "pop from empty list") end
            local raw_idx = (args and not is_none(args[1])) and unwrap_int(args[1]) or nil
            local idx = raw_idx and (raw_idx < 0 and (len + raw_idx + 1) or (raw_idx + 1)) or len
            if idx < 1 or idx > len then require("Module:PythonScript/Runtime").raise("IndexError", "pop index out of range") end
            return table.remove(obj.elements, idx)
        end }
        elseif attr_name == "remove" and obj.__type == "list" then return { __type = "builtin_method", __name = "remove", __call = function(args)
            local val = args[1]
            for i, v in ipairs(obj.elements) do if is_eq(v, val) then table.remove(obj.elements, i); return nil end end
            require("Module:PythonScript/Runtime").raise("ValueError", "list.remove(x): x not in list")
        end }
        elseif attr_name == "clear" and obj.__type == "list" then return { __type = "builtin_method", __name = "clear", __call = function() obj.elements = {}; return nil end }
        elseif attr_name == "index" then return { __type = "builtin_method", __name = "index", __call = function(args)
            local val = args[1]
            local s_pos, e_pos = resolve_search_bounds(#obj.elements, args[2], args[3])
            for i = s_pos, math.min(e_pos, #obj.elements) do
                if is_eq(obj.elements[i], val) then return i - 1 end
            end
            require("Module:PythonScript/Runtime").raise("ValueError", "item is not in " .. obj.__type)
        end }
        elseif attr_name == "count" then return { __type = "builtin_method", __name = "count", __call = function(args)
            local val, c = args[1], 0
            for _, v in ipairs(obj.elements) do if is_eq(v, val) then c = c + 1 end end
            return c
        end }
        elseif attr_name == "reverse" and obj.__type == "list" then return { __type = "builtin_method", __name = "reverse", __call = function()
            local n = #obj.elements
            for i = 1, math.floor(n / 2) do obj.elements[i], obj.elements[n - i + 1] = obj.elements[n - i + 1], obj.elements[i] end
            return nil
        end }
        elseif attr_name == "copy" and obj.__type == "list" then return { __type = "builtin_method", __name = "copy", __call = function()
            local res = {__type="list", elements={}}; for _, v in ipairs(obj.elements) do table.insert(res.elements, v) end; return res
        end }
        elseif attr_name == "sort" and obj.__type == "list" then
            return { __type = "builtin_method", __name = "sort", __call = function(args, kwargs)
                local Runtime = require("Module:PythonScript/Runtime")
                local Evaluator = require("Module:PythonScript/Evaluator")
                local EvalUtils = require("Module:PythonScript/Evaluator/Utils")

                local key_func = kwargs and kwargs.key
                if is_none(key_func) then key_func = nil end
                local reverse = kwargs and kwargs.reverse
                reverse = (reverse ~= nil and not is_none(reverse)) and Runtime.builtins.bool.__call({reverse}) or false

                local decorated = {}
                for i, v in ipairs(obj.elements) do
                    local k = key_func and Evaluator.invoke(key_func, {v}, {}) or v
                    decorated[i] = { key = k, orig_idx = i, val = v }
                end

                table.sort(decorated, function(a, b)
                    if is_eq(a.key, b.key) then return a.orig_idx < b.orig_idx end
                    local cmp = EvalUtils.py_cmp(a.key, b.key)
                    if reverse then return cmp > 0 else return cmp < 0 end
                end)

                for i, d in ipairs(decorated) do obj.elements[i] = d.val end
                return nil
            end }
        elseif attr_name == "__iter__" then return { __type = "builtin_method", __call = function() return require("Module:PythonScript/Runtime").builtins.iter({obj}) end }
        elseif attr_name == "__len__" then return { __type = "builtin_method", __name = "__len__", __call = function() return #(obj.elements or {}) end }
        elseif attr_name == "__contains__" then return { __type = "builtin_method", __name = "__contains__", __call = function(args)
            local v = args[1]
            for _, e in ipairs(obj.elements) do if is_eq(e, v) then return true end end
            return false
        end }
        elseif attr_name == "__getitem__" then return { __type = "builtin_method", __name = "__getitem__", __call = function(args)
            local idx = unwrap_int(args[1])
            local len = #obj.elements
            if type(idx) == "number" and math.floor(idx) == idx then
                if idx < 0 then idx = len + idx end
                if idx < 0 or idx >= len then require("Module:PythonScript/Runtime").raise("IndexError", "index out of range") end
                return obj.elements[idx + 1]
            end
            require("Module:PythonScript/Runtime").raise("TypeError", "indices must be integers")
        end }
        elseif attr_name == "__setitem__" and obj.__type == "list" then return { __type = "builtin_method", __name = "__setitem__", __call = function(args)
            local idx, val = unwrap_int(args[1]), args[2]
            local len = #obj.elements
            if type(idx) == "number" and math.floor(idx) == idx then
                if idx < 0 then idx = len + idx end
                if idx < 0 or idx >= len then require("Module:PythonScript/Runtime").raise("IndexError", "list assignment index out of range") end
                obj.elements[idx + 1] = val
                return require("Module:PythonScript/Runtime").builtins.None
            end
            require("Module:PythonScript/Runtime").raise("TypeError", "list indices must be integers")
        end }
        elseif attr_name == "__delitem__" and obj.__type == "list" then return { __type = "builtin_method", __name = "__delitem__", __call = function(args)
            local idx = unwrap_int(args[1])
            local len = #obj.elements
            if type(idx) == "number" and math.floor(idx) == idx then
                if idx < 0 then idx = len + idx end
                if idx < 0 or idx >= len then require("Module:PythonScript/Runtime").raise("IndexError", "list assignment index out of range") end
                table.remove(obj.elements, idx + 1)
                return require("Module:PythonScript/Runtime").builtins.None
            end
            require("Module:PythonScript/Runtime").raise("TypeError", "list indices must be integers")
        end }
        end
    end
    return nil
end
return Utils