首页 > 其他分享 >aardio教程三) 元表、元方法

aardio教程三) 元表、元方法

时间:2024-03-20 10:33:26浏览次数:23  
标签:__ function 教程 元表 函数 aardio messageId var 定义

前言

还有个迭代器,基础语法基本已经说完了,后面想到啥再补充,之后的教程会从以下方面来讲:

  • 基础库的使用,比如string、table等
  • 基础控件的使用,比如listview、tab等
  • aardio和Python交互,比如给Python写个界面
  • 自带的范例程序
  • 我写的一些小程序

当然,我的理解也是很基础的,特别是在界面设计上,我都是用的默认控件的默认设置,不会去自定义控件内容。要想做出特别炫酷的程序,你还得依赖其他语言和工具的基础。例如用HTML和CSS来实现界面。

元表、元方法

参考文档:

  • https://bbs.aardio.com/doc/reference/libraries/kernel/table/meta.html
  • https://bbs.aardio.com/doc/reference/the%20language/operator/overloading.html

主要是用于重载运算符和内置函数的行为。

表可以定义另一个表作为元表,然后在元表里定义元方法来定义操作符或内置函数操作表的一些行为。这种类似于Python的魔法方法,在Python中使用__eq__定义==的行为,而在aardio中用_eq元方法来定义==的行为。

初级使用例子

举个例子,python中print会调用对象的__str____repr__来打印一个对象,而aardio也是调用tostring来打印一个对象,但表默认并没有定义_tostring元方法,导致打印出来的内容是table: 03B2E3A8的格式

我们可以通过给表定义_tostring元方法,来使io.print或者console.log正常显示表

import console; 
io.open()
var tab = {
	a=1;
	b=2;
	@{
		_tostring = function(...) {
		    // 元方法里不能调用触发元方法的函数,比如_tostring里不能调用tostring
		    // _get元方法可以通过[[k]]运算符来避开元方法,通过.和[]会触发_get,而[[]]不会
			return table.tostring(owner);
		}
	}
}
io.print("没有定义元方法" , {});
io.print("定义了元方法" , tab);
console.pause(true);

输出如下:

没有定义元方法  table: 03A8E2F8
定义了元方法    {
a=1;
b=2
}

运算符重载

这个就不细说了,应该很容易理解。

io.open(); tab = { x=10 ; y=20 };
tab2 = { x=12 ; y=22 }
//c = tab + tab2; //这样肯定会出错,因为 table默认是不能相加的

//创建一个元素,元表中的__add函数重载加运算符。
tab@ = {
	_add = function(b) { 
		return owner.x + b.x
	};
}

c = tab + tab2; //这时候会调用重载的操作符 tab@._add(tab2)
io.print( c ) //显示22

入门使用例子

还有一个很常用的元方法是_get_set,是定义访问对象属性时触发的。利用这个可以让代码量少很多,看起来逻辑也更清晰。

这里举个实际例子,我在封装sunny的时候,遇到个很累人的事。sunny的dll导出函数,返回值有些是指针,你需要手动给他转成字符串,而且还需要手动释放这个指针指向的内存,也就是说你调用一次导出函数,就得写至少三行代码(调用、转字符串和释放)。

那么,有没有一种方法,定义完这个导出函数,在使用的时候就调用函数释放内存,并转成字符串返回,而不用我每次都手动释放和转字符串。

先定义request类,现在只需要给它定义一个messageId属性和_meta元方法:

namespace sunny;

class request{
	ctor(messageId){
		this.messageId = messageId;
	}
	@_meta;
	
}

@后面跟的是元表的名称,你可以将元表定义在名字空间里,这样看起来代码更舒服。下面在类的名字空间里定义dll方法和元表.

namespace request{
    //释放指针的函数
	Free = ::SunnyDLL.api("Free","void(pointer p)");
	// 下面的函数第一个参数都是messageId
    DelRequestHeader = ::SunnyDLL.api("DelRequestHeader","void(int id,str h)");
    GetRequestBodyLen = ::SunnyDLL.api("GetRequestBodyLen","int(int id)");
	GetRequestBody = ::SunnyDLL.api("GetRequestBody","pointer(int id)");
	// 定义一个中间方法
    // name是要调用的导出函数,messageId则是导出函数的第一个参数
	xcall = function(name, messageId, len){
		var func = self[name];
		if(!func) error("不支持的函数!");
		function proxyFunc(...){
			var v = func(messageId, ...);
			var result;
			if(type(v) == type.pointer){
				if(len) result = ..raw.tostring(v,1,len);
				else result = ..raw.tostring(v);
				Free(v);
			}else{
				result = v;
			}
			return result;
		}
		
		return proxyFunc;
	}
	// 定义元表
	_meta = {
		_get = function(k){
			return xcall(k, owner.messageId);
		}
		
	}
}

这个代码初看可能有点费劲,我们拆解着来看。

首先前面几行只是定义了四个dll的导出函数,然后下面定义了_meta这个表。

而_meta里只定义了一个元方法_get,它的作用是当你访问对象的属性时会触发这个方法,然后给你返回值。比如我先实例化一个request对象

r = request(111111);
// 当访问r.GetRequestBody时,这个对象没有GetRequestBody属性,所以会触发_get元方法
// 得到的返回值就是 返回它的返回值也就是`xcall("GetRequestBody", owner.messageId)`.
console.log(r.GetRequestBody)

这里的owner就是指r这个对象。然后定义了xcall这个函数,它里面又定义了一个函数proxyFunc,并将它作为返回值,这种被称为闭包。先分析下xcall方法

// 这里的self指的是当前名字空间,也就是request,name则是需要调用的方法名,例如是GetRequestBody
// 这里func的值就等于GetRequestBody,也就是::SunnyDLL.api("GetRequestBody","pointer(int id)");
var func = self[name];
// 如果func是null的话,说明当前名字空间下没有这个函数,也就不是我们定义的sunny导出函数
if(!func) error("不支持的函数!");
// 定义了proxyFunc函数,`xcall(k, owner.messageId)`返回的值就是proxyFunc函数,这里的三个点表示传入任意个参数,类似于Python中的*args
function proxyFunc(...){
    // 调用GetRequestBody(messageId, ...)
	var v = func(messageId, ...);
	// 定义返回结果
	var result;
	// 如果结果是指针的话
	if(type(v) == type.pointer){
	    // 就把它转为字符串,二进制数据需要指定长度,不然就是到\0结束
		if(len) result = ..raw.tostring(v,1,len);
		else result = ..raw.tostring(v);
		// 调用导出函数释放内存
		Free(v);
	}else{
	    // 如果是其他类型数据就直接返回,比如数值或null
		result = v;
	}
	return result;
}

这样一番折腾,起了什么效果呢,看一下下面两段代码,如果不利用元方法的话,你使用dll导出函数得这么写

// 导入request名字空间
improt request;
// 调用名字空间下的函数
var messageId = 111111
var pResult = request.GetRequestBody(messageId);
// 将指针转为字符串
var result = raw.tostring(pResult,1);
// 释放内存
request.Free(pResult);
// 再使用其他导出函数也需要重复写这几行代码

看着就几行代码,但是你想想调用一个函数都得写好几行,如果调用多次呢。而定义了xcall和_meta之后,只需要这样写代码:

improt request;
var messageId = 111111;
var req = request(messageId);
var result = req.GetRequestBody();
// 后面调用都只需要用req.方法名()调用,不需要管raw.tostring和Free了

因为req是可以复用的,所以我调用任何导出函数都只需要写一行代码,使用sunny库的代码也变得更简洁易懂了。

官方例子

给表创建一个代理,监控表属性的访问和设置:

// 创建一个代理,为另一个table对象创建一个替身以监控对这个对象的访问
function table.createProxy(tab) {
    var real = tab;//在闭包中保存被代理的数据表tab
    var _meta = {
        _get = function(k){
            io.print(k+"被读了");
            return real[k];
        };
        _set = function (k,v){
            io.print(k+"被修改值为"+v)
            real[k]=v; //删除这句代码就创建了一个只读表
        }
    }
    var proxy = {@_meta};//创建一个代理表
    
    return proxy; //你要访问真正的表?先问过我吧,我是他的经纪人!!!
}

//下面是使用示例

tab = {x=12;y=15};
proxy = table.createProxy(tab);//创建一个代理表,以管理对tab的存取访问

io.open();
c = proxy.x; //显示 "x被读了"
proxy.y = 19; //显示 "y被修改值为19"
io.print(proxy.y); //显示 "y被读了" 然后显示19

所有的元方法

元方法/属性 函数定义 Python中的魔法方法 说明
_weak 用不到
_type 属性 type(obj)函数的行为
_readonly 属性 等于false,_开头的成员也不是只读属性
_defined 感觉没啥用
_keys 属性 可用于table.keys等函数动态获取对象的键名列表(例如动态生成键值对的外部JS对象可使用这个元方法返回成员名字列表
_startIndex 属性 用于table.eachIndex等函数动态指定数组的开始下标。
_get function(k,ownerCall) __getattr____getitem__ 如果读取表中不存在的键会触发_get元方法并返回值
_set function(k,v) __setattr____setitem__ 当你给表的一个缺少的键赋值时会触发_set元方法
_tostring function(...) __str____repr__ tostring(obj, ...)
_tonumber function() tonumber(obj)
_json function() web.json.stringify(obj),可返回一个可被转化为json的值。或者返回一个字符串和true
_toComObject 用于自定义一个表对象如何转换为 COM 对象,可定义为函数,也可以直接定义为对象
_eq function(b) __eq____ne__ ==!=,比较对象时,两个对象的_eq必须是同一个
_le function(b) __le____ge__ <=>=
_lt function(b) __lt____gt__ <>
_add function(b) __add__ +
_sub function(b) __sub__ -
_mul function(b) __mul__ *
_div function(b) __truediv__ /
_lshift function(b) __lshift__ << 左移
_rshift function(b) __rshift__ >> 右移
_mod function(b) __mod__ % 取模
_pow function(b) __pow__ **幂运算
_unm function() __neg__ - 负号
_len function() __len__ #取长运算符,Python中则为len函数
_concat function(b) ++ 连接运算符
_call function(...) __call__ 对象当函数来调用

属性元表

不仅可以给对象定义元表,也可以给对象的属性定义一个元表,有点类似于Python中的property,可以控制属性修改和获取的行为。

如果要看例子的话,可以在aardio的目录全局搜下@_metaProperty

以使用最多的属性text为例,基本每个控件都有一个text属性,你可以很方便的通过.text获取和修改空间显示的文字。

其实不用属性元表也能实现这个效果,代码如下:

import console; 
class staticText{
	getText = function(){
		..console.log("获取到界面文本内容")
	};
	setText = function(v){
		..console.log("将文本("+v+")显示到界面控件上")
	}
	@_meta;
}

namespace staticText{
    _meta = {
        _get = function(k){
        	if(k == "text"){
        	    return owner.getText();
        	}
        };
        _set = function(k,v){
        	if(k == "text"){
        	    return owner.setText(v);
        	}
        }    
    }
}

s = staticText()
console.log(s.text);
s.text = "修改文本";
console.pause(true);

但是如果属性多了的话,就需要一堆的if来判断属性,所以aardio作者就引入了metaProperty这个功能。这样写的代码看起来更简洁和清晰,用法如下:

import console; 
import util.metaProperty;

class staticText{
	getText = function(){
		..console.log("获取到界面文本内容")
	};
	setText = function(v){
		..console.log("将文本("+v+")显示到界面控件上")
	}
	@_metaProperty;
}

namespace staticText{
    _metaProperty = ..util.metaProperty(
        text = {
            _get = function(){
            	return owner.getText();
            };
            _set = function(v){
            	return owner.setText(v);
            }
        };
        // 可以写其他属性
    );
    // 可以打印下_metaProperty看看
    ..console.dump(_metaProperty);
}

s = staticText()
console.log(s.text);
s.text = "修改文本";
console.pause(true);

本文由博客一文多发平台 OpenWrite 发布!

标签:__,function,教程,元表,函数,aardio,messageId,var,定义
From: https://www.cnblogs.com/kanadeblisst/p/18084708

相关文章

  • 商汤xtuner微调教程(搬运工,在官方文档讲述的很好)
    怎么说呢,祝大家炼丹愉快吧~......
  • JSON Web Token 入门教程
    本文收录于Github.com/niumoo/JavaNotes,Java系列文档,数据结构与算法!本文收录于网站:https://www.wdbyte.com/,我的公众号:程序猿阿朗JSONWebToken(JWT)是一种可以在多方之间安全共享数据的开放标准,JWT数据经过编码和数字签名生成,可以确保其真实性,也因此JWT通常用于身份认证......
  • Aria2安装教程
    Aria2安装教程时间:2024-3-1922:41:03版本:1.37.0官方下载地址:Releases·aria2/aria2(github.com)打开网址后点击这里。​​拉到最后,选择自己合适的版本下载即可,我选择的是win64版本。​​解压文件夹,放到自己合适的目录下。​​在当前目录新建四个文件:Aria2.log(日志......
  • 01 | Swoole与Go系列教程之HTTP服务的应用
    首发原文链接:Swoole与Go系列教程之HTTP服务的应用大家好,我是码农先森。写在前面PHP曾是Web开发领域佼佼者,随着业务壮大,异步和高并发方面不足显现。Swoole曾经尝试填补空白,但局限性也比较的明显。Go语言的崛起,简洁语法和并发优势吸引大厂使用,吸引了大多数程序员的转......
  • 使用百度智能云千帆 AppBuilder 创建一个简单的AI应用教程,还可以配置到微信客服和微信
    目录一、前言二、使用教程三、案例1、描述词创建应用2、发布应用链接效果图一、前言国内想要创建一个自己的AI工具,除了使用openAI接口外,其实还可以使用百度文心一言的接口,那就是百度智能云的千帆平台。毕竟如果使用openAI的接口的话,有很多成本在内的,这个我就不方便......
  • SQL-Labs靶场“36-37”关通关教程
    一、36关GET单引号宽字节注入请求方式注入类型拼接方式GET联合、报错、布尔盲注、延时盲注id=‘$id’首先我们进行测试(使用?id=1\,查看过滤后的回显)这里可以看到对我们的注释符进行了注释以及单双引号进行测试会发现都是如此:所以这里我们判断使用了过滤函数进行了过滤......
  • 信息收集模块(四)masscan介绍和使用教程
    一、masscan介绍    masscan是号称最快的互联网的端口扫描工具,最快几分钟就可以扫描完毕    但是他和nmap比较内饰但是masscan更加灵活,它允许自定义任意的地址范和端口范围。 二、masscan工具的命令使用        IP地址范围,有三种有效格式,1、......
  • Python教程:如何向Word中添加表格
    简介MicrosoftWord是一种流行的文档处理软件,广泛用于创建各种类型的文档,包括报告、简历、手册等。Python提供了许多库来处理MicrosoftWord文档,其中包括python-docx,它使我们能够轻松地创建、修改和操作Word文档。本文将介绍如何使用Python的python-docx库向Word文档中添加表格......
  • RestCloud数据集成平台-Windows全量包安装部署详细教程
     1.安装准备1.1服务器硬件环境要求RestcloudDataOps服务器的最低运行环境如下:CPU:Intel1.6GHz4核或以上内存:可用内存4G或以上(不包括操作系统等其他程序占用内存)可用硬盘空间:40G或以上最少服务器数量:1台1.2服务器端软件环境要求支持操作系统:Windows11/Windows10/W......
  • 自动化办公:Python如何操控Excel(详细教程)
    1.准备环境Python版本:3.6.5IDE集成开发环境:pycharmPython库选择:openpyxlopenpyxl操作的excel文件以xlsx结尾。openpyxl官网基础命令查看Python版本python--version查看pip版本pip--version安装openxlsxpipinstallopenpyxl-ihttps://pypi.tuna.......