-
VB新手入门知识之一
1.1知识要点1.VB的特点、安装和启动了解VB面向对象、可视化、事件驱动的特点。2.VB集成开发环境初学者主要掌握菜单栏、工具栏、工具箱窗口、属性窗口、代码窗口、工程资源管理器窗口的使用。3.VB对象的概念,对象的属性、方法和事件三要素4.创建VB应用程序的过程5.掌握窗体、标签、文本框、命令按钮控件的常用属性、方法和事件的使用6.VB应用程序的构成和管理VB应用程序的组成:一个.vbp工程文件、一个(或多个).frm窗体文件、自动产生的.frx二进制文件、还可以有.bas及.cis文件(见下图)。掌握在完成一个应用程序的创建、编辑、调试后,保存时不要遗漏了某个文件;掌握在工程中添加或删除窗体的方法;区分窗体名称和窗体文件名。7.制作安装盘打包和展开的作用和操作过程,查看打包和展开的结果文件。8.VB帮助系统的安装和使用VB帮助系统盘MSDN的安装;使用VB帮助最方便的方法是选中需帮助的对象,按F1键,即可显示该对象的帮助信息。1.2常见错误和难点分析1.标点符号错误在VB中只允许使用西文标点,任何中文标点符号在程序编译时产生"无效字符"错误,统在该行以红色字显示。用户在进入VB后不要使用中文标点符号。中、西文状态下标点号对照见下表所示。中、西状态下标点符号对照西文状态,.'";_、>=、
-
内存分配与回收策略
Java自动内存管理的两个问题:给对象分配内存回收分配给对象的内存对象的内存分配,往大方向讲,就是在堆上分配,对象主要分配在新生代的Eden区上,如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配。少数情况下也可能直接分配在老年代中,分配的规则不是百分之百固定的,其细节在于使用哪种垃圾收集器组合,还有虚拟机中与内存相关的参数。对象优先在Eden分配绝大多数情况,对象都在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机会发生一次MinorGC。虚拟机提供了-XX:+PrintGCDetails这个收集器日志参数,告诉虚拟机在发生垃圾收集行为时打印内存回收日志,并且在进程退出的时候输出当前的内存各区域分配情况。在实际应用中,内存回收日志一般是打印到文件后通过日志工具进行分析,不过本实验的日志并不多,直接阅读就能看的很清楚。测试内存分配代码:publicclassTestAllocation{privatestaticfinalint_1MB=1024*1024;/***VM参数:-XX:+UseSerialGC-verbose:gc-Xms20M-Xmx20M-Xmn10M-XX:+PrintGCDetails-XX:SurvivorRatio=8*/publicstaticvoidtestAllocation(){byte[]allocation1,allocation2,allocation3,allocation4;allocation1=newbyte[2*_1MB];allocation2=newbyte[2*_1MB];allocation3=newbyte[2*_1MB];allocation4=newbyte[4*_1MB];//出现一次MinorGC}publicstaticvoidmain(String[]args){TestAllocation.testAllocation();}}1234567891011121314151617运行结果:[GC(AllocationFailure)[DefNew:7129K->531K(9216K),0.0037131secs]7129K->6675K(19456K),0.0137564secs][Times:user=0.00sys=0.00,real=0.01secs]Heapdefnewgenerationtotal9216K,used4709K[0x00000000fec00000,0x00000000ff600000,0x00000000ff600000)edenspace8192K,51%used[0x00000000fec00000,0x00000000ff014930,0x00000000ff400000)fromspace1024K,51%used[0x00000000ff500000,0x00000000ff584c58,0x00000000ff600000)tospace1024K,0%used[0x00000000ff400000,0x00000000ff400000,0x00000000ff500000)tenuredgenerationtotal10240K,used6144K[0x00000000ff600000,0x0000000100000000,0x0000000100000000)thespace10240K,60%used[0x00000000ff600000,0x00000000ffc00030,0x00000000ffc00200,0x0000000100000000)Metaspaceused2573K,capacity4486K,committed4864K,reserved1056768Kclassspaceused285K,capacity386K,committed512K,reserved1048576K注意:使用64位的需要添加参数-XX:+UseSerialGC,使其强制使用SerialGC以上的方法试图分配3个2MB大小和一个4MB大小的对象,在运行通过-Xms20M-Xmx20M-Xmn10M三个参数限制了Java堆大小为20MB,不可扩展,其中10MB分配给新生代,剩下的10MB分配给老年代。-XX:SurvivorRatio=0决定了新生代中Eden区与一个Survivor区的空间比例为8:1,从输出的结果也可以清晰地看到“edenspace8192K、fromspace1024K、tospace1024K”的信息,新生代的总可用空间为9216KB(Eden区+一个Survivor区的总容量)。执行testAllocation()中分配allocation4的语句时会发生一次MinorGC,这次GC的结果时新生代6651KB变为148KB,而总内存占用几乎没有减少(因为allocation1、allocation2、allocation3三个对象都是存活的,虚拟机几乎没有找到可回收的对象)。这次GC发生的原因是给allocation4分配内存的时候,发现Eden已经被占用了6MB。剩余空间已经不足以分配allocation4所需的4MB内存,因此发生MinorGC。GC期间虚拟机又发现已有的3个2MB大小的对象全部无法放入Survivor空间(仅有1MB),所以只好通过分配担保的机制提前转移到老年代去了。这次GC结束后,4MB的allocation4的对象顺利分配在Eden中,因此程序执行完的结果是Eden占用4MB(被allocation4占用),Survivor空闲,老年代被占用6MB(被allocation1allocation2allocation3占用)。通过GC日志可以证实这点。MinorGC和FullGC不同点:MinorGC:指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生夕灭的特性,所以MinorGC非常频繁,一般回收速度也比较快。。MajorGC/FullGC:指发生在老年代的GC,出现了MajorGC,经常会伴随至少一次的MinorGC(但非绝对的,在ParallelScavenge收集器的收集策略里就有直接进行MajorGC的策略选择过程)。MajorGC的速度一般会比MinorGC慢10倍以上。大对象直接进入老年代所谓大对象就是指,需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串及数组(笔者例子中的byte[]数组就是典型的大对象)。大对象对虚拟机的内存分配来说就是一个坏消息(替Java虚拟机抱怨一句,比遇到一个大对象更加坏的消息就是遇到一群“朝生夕灭”的“短命大对象”,写程序的时候应当避免),经常出现大对象容易导致内存还有不少空间时就提前触发垃圾收集以获取足够的连续空间来“安置”它们。虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代中分配。这样做的目的是避免在Eden区及两个Survivor区之间发生大量的内存拷贝(复习一下:新生代采用复制算法收集内存)。代码:publicclassTestPretensureSizeThreshold{privatestaticfinalint_1MB=1024*1024;/***VM参数:-XX:+UseSerialGC-verbose:gc-Xms20M-Xmx20M-Xmn10M-XX:+PrintGCDetails-XX:SurvivorRatio=8*-XX:PretenureSizeThreshold=3145728*/publicstaticvoidtestPretenureSizeThreshold(){byte[]allocation;allocation=newbyte[4*_1MB];//直接分配在老年代中}publicstaticvoidmain(String[]args){TestPretensureSizeThreshold.testPretenureSizeThreshold();}}1234567891011121314151617运行结果:Heapdefnewgenerationtotal9216K,used1148K[0x00000000fec00000,0x00000000ff600000,0x00000000ff600000)edenspace8192K,14%used[0x00000000fec00000,0x00000000fed1f3f0,0x00000000ff400000)fromspace1024K,0%used[0x00000000ff400000,0x00000000ff400000,0x00000000ff500000)tospace1024K,0%used[0x00000000ff500000,0x00000000ff500000,0x00000000ff600000)tenuredgenerationtotal10240K,used4096K[0x00000000ff600000,0x0000000100000000,0x0000000100000000)thespace10240K,40%used[0x00000000ff600000,0x00000000ffa00010,0x00000000ffa00200,0x0000000100000000)Metaspaceused2572K,capacity4486K,committed4864K,reserved1056768Kclassspaceused285K,capacity386K,committed512K,reserved1048576K执行如上代码的testPretenureSizeThreshold()方法后,我们看到Eden空间几乎没有被使用,而老年代10MB的空间被使用了40%,也就是4MB的allocation对象直接就分配在老年代中,这是因为PretenureSizeThreshold被设置为3MB(就是3145728B,这个参数不能与-Xmx之类的参数一样直接写3MB),因此超过3MB的对象都会直接在老年代中进行分配。长期存活的对象进入老年代既然虚拟机采用了分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放在新生代,哪些对象应放在老年代中。为了做到这点,虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在Eden出生并经过第一次MinorGC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1。对象在Survivor区中每“熬过”一次MinorGC,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁),就将会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold设置。读者可以试试分别以-XX:MaxTenuringThreshold=1和-XX:MaxTenuringThreshold=15两种设置来执行代码清单3-7中的testTenuringThreshold()方法,此方法中的allocation1对象需要256KB内存,Survivor空间可以容纳。当MaxTenuringThreshold=1时,allocation1对象在第二次GC发生时进入老年代,新生代已使用的内存GC后非常干净地变成0KB。而MaxTenuringThreshold=15时,第二次GC发生后,allocation1对象则还留在新生代Survivor空间,这时新生代仍然有1MB被占用。代码:publicclassTestTenuringThreshold{privatestaticfinalint_1MB=1024*1024;/***VM参数:-XX:+UseSerialGC-verbose:gc-Xms80M-Xmx80M-Xmn40M-XX:+PrintGCDetails-XX:SurvivorRatio=8-XX:MaxTenuringThreshold=1*-XX:+PrintTenuringDistribution*/@SuppressWarnings("unused")publicstaticvoidtestTenuringThreshold(){byte[]allocation1,allocation2,allocation3;allocation1=newbyte[_1MB];//什么时候进入老年代取决于XX:MaxTenuringThreshold设置allocation2=newbyte[16*_1MB];allocation3=newbyte[16*_1MB];allocation3=null;allocation3=newbyte[16*_1MB];}publicstaticvoidmain(String[]args){TestTenuringThreshold.testTenuringThreshold();}}1234567891011121314151617181920212223以MaxTenuringThreshold=1得到的结果:[GC(AllocationFailure)[DefNew:19374K->1555K(36864K),0.0073413secs]19374K->17939K(77824K),0.0073791secs][Times:user=0.00sys=0.00,real=0.01secs][GC(AllocationFailure)[DefNew:17939K->0K(36864K),0.0016595secs]34323K->17938K(77824K),0.0016844secs][Times:user=0.00sys=0.00,real=0.00secs]Heapdefnewgenerationtotal36864K,used16712K[0x00000000fb000000,0x00000000fd800000,0x00000000fd800000)edenspace32768K,51%used[0x00000000fb000000,0x00000000fc052040,0x00000000fd000000)**fromspace4096K,0%**used[0x00000000fd000000,0x00000000fd000000,0x00000000fd400000)tospace4096K,0%used[0x00000000fd400000,0x00000000fd400000,0x00000000fd800000)tenuredgenerationtotal40960K,used17938K[0x00000000fd800000,0x0000000100000000,0x0000000100000000)**thespace40960K,43%**used[0x00000000fd800000,0x00000000fe984970,0x00000000fe984a00,0x0000000100000000)Metaspaceused2573K,capacity4486K,committed4864K,reserved1056768Kclassspaceused285K,capacity386K,committed512K,reserved1048576K1234567891011以MaxTenuringThreshold=15得到的结果:[GC(AllocationFailure)[DefNewDesiredsurvivorsize2097152bytes,newthreshold15(max15)-age1:1592488bytes,1592488total:19374K->1555K(36864K),0.0089774secs]19374K->17939K(77824K),0.0090155secs][Times:user=0.00sys=0.00,real=0.01secs][GC(AllocationFailure)[DefNewDesiredsurvivorsize2097152bytes,newthreshold15(max15)-age2:1591648bytes,1591648total:17939K->1554K(36864K),0.0011282secs]34323K->17938K(77824K),0.0011471secs][Times:user=0.00sys=0.00,real=0.00secs]Heapdefnewgenerationtotal36864K,used18266K[0x00000000fb000000,0x00000000fd800000,0x00000000fd800000)edenspace32768K,51%used[0x00000000fb000000,0x00000000fc052040,0x00000000fd000000)fromspace4096K,37%used[0x00000000fd000000,0x00000000fd184960,0x00000000fd400000)tospace4096K,0%used[0x00000000fd400000,0x00000000fd400000,0x00000000fd800000)tenuredgenerationtotal40960K,used16384K[0x00000000fd800000,0x0000000100000000,0x0000000100000000)thespace40960K,40%used[0x00000000fd800000,0x00000000fe800010,0x00000000fe800200,0x0000000100000000)Metaspaceused2572K,capacity4486K,committed4864K,reserved1056768Kclassspaceused285K,capacity386K,committed512K,reserved1048576K1234567891011121314151617动态对象年龄判断对象的年龄到达了MaxTenuringThreshold可以进入老年代,同时,如果在survivor区中相同年龄所有对象大小的总和大于survivor区的一半,年龄大于等于该年龄的对象就可以直接进入老年代。无需等到MaxTenuringThreshold中要求的年龄。具体代码如下:publicclassAllocationTest2{privatestaticfinalint_1MB=1024*1024;/**-Xms20M-Xmx20M-Xmn10M-XX:SurvivorRatio=8-XX:+PrintGCDetails-XX:+UseSerialGC-XX:MaxTenuringThreshold=15-XX:+PrintTenuringDistribution**/publicstaticvoidtestTenuringThreshold2(){byte[]allocation1,allocation2,allocation3,allocation4;allocation1=newbyte[_1MB/4];allocation2=newbyte[_1MB/4];allocation3=newbyte[4*_1MB];allocation4=newbyte[4*_1MB];allocation4=null;allocation4=newbyte[4*_1MB];}publicstaticvoidmain(String[]args){testPretenureSizeThreshold2();}}1234567891011121314151617181920212223242526得到结果:[GC(AllocationFailure)[DefNewDesiredsurvivorsize524288bytes,newthreshold1(max15)-age1:1048576bytes,1048576total:5592K->1024K(9216K),0.0026321secs]5592K->5139K(19456K),0.0026687secs][Times:user=0.00sys=0.00,real=0.00secs][GC(AllocationFailure)[DefNewDesiredsurvivorsize524288bytes,newthreshold15(max15):5120K->0K(9216K),0.0009009secs]9235K->5139K(19456K),0.0009171secs][Times:user=0.00sys=0.00,real=0.00secs]Heapdefnewgenerationtotal9216K,used4178K[0x00000000fec00000,0x00000000ff600000,0x00000000ff600000)edenspace8192K,51%used[0x00000000fec00000,0x00000000ff014930,0x00000000ff400000)fromspace1024K,0%used[0x00000000ff400000,0x00000000ff400000,0x00000000ff500000)tospace1024K,0%used[0x00000000ff500000,0x00000000ff500000,0x00000000ff600000)tenuredgenerationtotal10240K,used5139K[0x00000000ff600000,0x0000000100000000,0x0000000100000000)thespace10240K,50%used[0x00000000ff600000,0x00000000ffb04c20,0x00000000ffb04e00,0x0000000100000000)Metaspaceused2573K,capacity4486K,committed4864K,reserved1056768KclassspaceusedboolTenuredGeneration::promotion_attempt_is_safe(size_tmax_promotion_in_bytes)const{//老年代最大可用的连续空间size_tavailable=max_contiguous_available();//每次晋升到老年代的平均大小size_tav_promo=(size_t)gc_stats()->avg_promoted()->padded_average();//老年代可用空间是否大于平均晋升大小,或者老年代可用空间是否大于当此GC时新生代所有对象容量boolres=(available>=av_promo)||(available>=max_promotion_in_bytes);returnres;}
-
C#开发基础跨平台物联网通讯框架 ServerSuperIO
ServerSuperIO详细介绍ServerSuperIO简称SSIO,是一个C#跨平台物联网通讯框架。C#开发基础跨平台物联网通讯框架ServerSuperIO一.SSIO的特点轻型高性能通信框架,适用于多种应用场,轮询模式、自控模式、并发模式和单例模式。设备驱动、IO通道、控制模式场景协调统一。设备驱动内轩命令驱动器、命令缓存器、自定义参数和实时数据元素。框架平台支持按设备命令优先级别进行调度,保证高级别命令及时发送。一个设备驱动同时支持串口和网络两种通讯方式,可以监视IO通道数据。一个设备驱动,在网络通讯时可以支持TCPServer和TCPClient两种工作模式。内置显示视图接口,满足不同显示需求。内置服务组件接口,可以自定义完成OPC服务、4-20mA输出、LED大屏显示、短信服务、以及多功能网关服务。可以创建多服务实例,完成不同业务的拆分。支持跨平台部署,可以运行在Linux和Windows系统。二.SSIO概述SSIO通信框架的设计思想是在SuperIO(SIO)基础上发展而来,并没有高大上的技术,主要是工作经验的积累,适合于不同应用场景的物联网的数据采集与交互。SSIO和SIO并不是简单的对IO高性能的操作,而是设备驱动、IO通道、控制模式和实际硬件设备之间的协调机制,各方面之间无缝衔接和运行,也是为了解决现实工作和应用场景的一些痛点。软硬件之间的数据交互,并且面临着复杂的现场环境:(1)复杂的、多样的通讯协议。有标准的协议,例如:Modbus等,也有很多根据标准协议修改的协议格式、以及自定义协议格式,并且千差万别。对于不好的软件架构,疲于应对,增加设备或协议要对整个软件进行梳理,往往在此过程中出现新的问题或BUG。(2)针对不同用户对软件界面或功能的要求有很大不同,使之满足不同用户的显示要求,可以自定义数据显示界面。那么就需要提供显示视图接口,与设备驱动进行交互。(3)既然现场设备的数据被采集上来,那么就需要对其进行处理,不仅仅是保存、查询、报表等,还有:数据转发、数据输出(OPC、模拟量、大屏等)等。那么就需要提供服务性的接口,与设备驱动进行交互。(4)通讯链路的多种性,对于同一个设备可能要支持RS232/RS485/RS422、RJ45、3G/4G等通讯方式,所以对于一个设备要对应多种通讯方式(串口和网络),也给我们的开发造成很大的障碍。(5)设备驱动、IO通道和实际的现场硬件终端之间链路复杂,有可能:一个设备驱动对应一个IO通道、一个设备驱动对应多个IO通道、多个设备驱动对应一个IO通道等情况。(6)既然设备与服务端进行数据交互,那么就应该对设备的通讯状态、IO状态、以及设备本身的状态进行监控,这样设备才处于可维护状态。(7)软件各版本、以及软件与硬件之间的兼容性很差,管理起来错综复杂。在框架平台稳定的情况下,只需要更新设备驱动。为了解决以上诸多问题,开发一个软件框架,支持二次开发。在不对软件框架改动的情况下,能够很方便的接入设备、维护设备、集成设备、处理设备业务数据等。软件框架相对稳定,把容易变化的部分进行灵活设计。三.控制模式(1)轮询模式:当串口和网络通讯时都可以使用这种控制模式。当有多个设备连接到通讯平台时,通讯平台会轮询调度设备进行通讯任务。某一时刻只能有一个设备发送请求命令、等待接收返回数据,这个设备完成发送、接收(如果遇到超时情况,则自动返回)后,下一个设备才进行通讯任务,依次轮询设备。如下图:(2)并发模式:只有网络通讯时可以使用这种控制模式。并发通讯模式是集中发送所有设备的请求指令,框架是采用循环同步方式发送请求命令。还有进一步提高的机会,采用并行异步方式集中发送请求命令。硬件设备接收到指令后进行校验,校验成功后返回对应指令的数据,通讯平台异步监听到数据信息后,进行接收操作,然后再进行数据的分发、处理等。如下图:(3)自控模式:只有网络通讯时可以使用这种控制模式。自控通讯模式与并发通讯模式类似,区别在于发送指令操作交给设备驱动本身进行控制,或者说交给二次开发者,二次开发者可以通过时钟定时用事件驱动的方式发送指令数据。硬件设备接收到指令后进行校验,校验成功后返回对应指令的数据,通讯平台异步监听到数据信息后,进行接收操作,然后再进行数据的分发、处理等。自控通讯模式可以为二次开发者提供精确的定时请求实时数据机制,使通讯机制更灵活、自主,如果多个设备驱动使用同一个IO通道的话,时间控制会有偏差。如下图:(4)单例模式:只有网络通讯时可以使用这种控制模式。在一个服务实例内只能有一个设备驱动,相当于一个设备驱动对应着N多个硬件设备终端。更适合通讯的数据协议有固定的标准,以命令关键字处理不同的数据。适用于高并发的硬件终端设备主动上传数据,服务器端根据数据信息进行处理和返回相应的数据。如下图:四.跨平台Windows和Linux(1)Windows运行效果(2)Linux运行效果
-
理解 Python 迭代对象、迭代器、生成器
在了解Python的数据结构时,容器(container)、可迭代对象(iterable)、迭代器(iterator)、生成器(generator)、列表/集合/字典推导式(list,set,dictcomprehension)众多概念参杂在一起,难免让初学者一头雾水,我将用一篇文章试图将这些概念以及它们之间的关系捋清楚。容器(container)容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in,notin关键字判断元素是否包含在容器中。通常这类数据结构把所有的元素存储在内存中(也有一些特例,并不是所有的元素都放在内存,比如迭代器和生成器对象)在Python中,常见的容器对象有:list,deque,….set,frozensets,….dict,defaultdict,OrderedDict,Counter,….tuple,namedtuple,…str容器比较容易理解,因为你就可以把它看作是一个盒子、一栋房子、一个柜子,里面可以塞任何东西。从技术角度来说,当它可以用来询问某个元素是否包含在其中时,那么这个对象就可以认为是一个容器,比如list,set,tuples都是容器对象:Python123456>>>assert1in[1,2,3]#lists>>>assert4notin[1,2,3]>>>assert1in{1,2,3}#sets>>>assert4notin{1,2,3}>>>assert1in(1,2,3)#tuples>>>assert4notin(1,2,3)询问某元素是否在dict中用dict的中key:Python123>>>d={1:'foo',2:'bar',3:'qux'}>>>assert1ind>>>assert'foo'notind#'foo'不是dict中的元素询问某substring是否在string中:Python1234>>>s='foobar'>>>assert'b'ins>>>assert'x'notins>>>assert'foo'ins尽管绝大多数容器都提供了某种方式来获取其中的每一个元素,但这并不是容器本身提供的能力,而是可迭代对象赋予了容器这种能力,当然并不是所有的容器都是可迭代的,比如:Bloomfilter,虽然Bloomfilter可以用来检测某个元素是否包含在容器中,但是并不能从容器中获取其中的每一个值,因为Bloomfilter压根就没把元素存储在容器中,而是通过一个散列函数映射成一个值保存在数组中。可迭代对象(iterable)刚才说过,很多容器都是可迭代对象,此外还有更多的对象同样也是可迭代对象,比如处于打开状态的files,sockets等等。但凡是可以返回一个迭代器的对象都可称之为可迭代对象,听起来可能有点困惑,没关系,先看一个例子:Python12345678910111213>>>x=[1,2,3]>>>y=iter(x)>>>z=iter(x)>>>next(y)1>>>next(y)2>>>next(z)1>>>type(x)>>>type(y)这里x是一个可迭代对象,可迭代对象和容器一样是一种通俗的叫法,并不是指某种具体的数据类型,list是可迭代对象,dict是可迭代对象,set也是可迭代对象。y和z是两个独立的迭代器,迭代器内部持有一个状态,该状态用于记录当前迭代所在的位置,以方便下次迭代的时候获取正确的元素。迭代器有一种具体的迭代器类型,比如list_iterator,set_iterator。可迭代对象实现了__iter__方法,该方法返回一个迭代器对象。当运行代码:Python123x=[1,2,3]foreleminx:...实际执行情况是:反编译该段代码,你可以看到解释器显示地调用GET_ITER指令,相当于调用iter(x),FOR_ITER指令就是调用next()方法,不断地获取迭代器中的下一个元素,但是你没法直接从指令中看出来,因为他被解释器优化过了。Python123456789101112>>>importdis>>>x=[1,2,3]>>>dis.dis('for_inx:pass')10SETUP_LOOP14(to17)3LOAD_NAME0(x)6GET_ITER>>7FOR_ITER6(to16)10STORE_NAME1(_)13JUMP_ABSOLUTE7>>16POP_BLOCK>>17LOAD_CONST0(None)20RETURN_VALUE迭代器(iterator)那么什么迭代器呢?它是一个带状态的对象,他能在你调用next()方法的时候返回容器中的下一个值,任何实现了__iter__和__next__()(python2中实现next())方法的对象都是迭代器,__iter__返回迭代器自身,__next__返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常,至于它们到底是如何实现的这并不重要。所以,迭代器就是实现了工厂模式的对象,它在你每次你询问要下一个值的时候给你返回。有很多关于迭代器的例子,比如itertools函数返回的都是迭代器对象。生成无限序列:Python123456>>>fromitertoolsimportcount>>>counter=count(start=13)>>>next(counter)13>>>next(counter)14从一个有限序列中生成无限序列:Python12345678910>>>fromitertoolsimportcycle>>>colors=cycle(['red','white','blue'])>>>next(colors)'red'>>>next(colors)'white'>>>next(colors)'blue'>>>next(colors)'red'从无限的序列中生成有限序列:Python123456789>>>fromitertoolsimportislice>>>colors=cycle(['red','white','blue'])#infinite>>>limited=islice(colors,0,4)#finite>>>forxinlimited:...print(x)redwhitebluered为了更直观地感受迭代器内部的执行过程,我们自定义一个迭代器,以斐波那契数列为例:Python1234567891011121314151617classFib:def__init__(self):self.prev=0self.curr=1def__iter__(self):returnselfdef__next__(self):value=self.currself.curr+=self.prevself.prev=valuereturnvalue>>>f=Fib()>>>list(islice(f,0,10))[1,1,2,3,5,8,13,21,34,55]Fib既是一个可迭代对象(因为它实现了__iter__方法),又是一个迭代器(因为实现了__next__方法)。实例变量prev和curr用户维护迭代器内部的状态。每次调用next()方法的时候做两件事:为下一次调用next()方法修改状态为当前这次调用生成返回结果迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。生成器(generator)生成器算得上是Python语言中最吸引人的特性之一,生成器其实是一种特殊的迭代器,不过这种迭代器更加优雅。它不需要再像上面的类一样写__iter__()和__next__()方法了,只需要一个yiled关键字。生成器一定是迭代器(反之不成立),因此任何生成器也是以一种懒加载的模式生成值。用生成器来实现斐波那契数列的例子是:Python123456789deffib():prev,curr=0,1whileTrue:yieldcurrprev,curr=curr,curr+prev>>>f=fib()>>>list(islice(f,0,10))[1,1,2,3,5,8,13,21,34,55]fib就是一个普通的python函数,它特殊的地方在于函数体中没有return关键字,函数的返回值是一个生成器对象。当执行f=fib()返回的是一个生成器对象,此时函数体中的代码并不会执行,只有显示或隐示地调用next的时候才会真正执行里面的代码。生成器在Python中是一个非常强大的编程结构,可以用更少地中间变量写流式代码,此外,相比其它容器对象它更能节省内存和CPU,当然它可以用更少的代码来实现相似的功能。现在就可以动手重构你的代码了,但凡看到类似:Python12345defsomething():result=[]for...in...:result.append(x)returnresult都可以用生成器函数来替换:Python123defiter_something():for...in...:yieldx生成器表达式(generatorexpression)生成器表达式是列表推倒式的生成器版本,看起来像列表推导式,但是它返回的是一个生成器对象而不是列表对象。Python12345>>>a=(x*xforxinrange(10))>>>a>>>sum(a)285总结容器是一系列元素的集合,str、list、set、dict、file、sockets对象都可以看作是容器,容器都可以被迭代(用在for,while等语句中),因此他们被称为可迭代对象。可迭代对象实现了__iter__方法,该方法返回一个迭代器对象。迭代器持有一个内部状态的字段,用于记录下次迭代返回值,它实现了__next__和__iter__方法,迭代器不会一次性把所有元素加载到内存,而是需要的时候才生成返回结果。生成器是一种特殊的迭代器,它的返回值不是通过return而是用yield。
-
基于 OSGi 规范的 C#开发基础框架 OSGi.NET
这是实现的一套基于OSGi规范的C#基础框架-OSGi.NET,并且用Go语言初步实现了插件的管理平台-插件仓库。在几个中小型项目中有所应用(Winform、WPF),主要可以解决多人协作的开发规范与插件的管理问题。OSGi.NET框架是一个参照了OSGi规范的模块化管理框架。框架为应用程序扩展(组件(bundle))提供了一个标准环境。整个框架可以划分为一些层次:运行环境模块(Bundle)生命周期管理服务注册扩展点支持目前OSGi.NET具有如下特色:组件的可插拔性:组件可根据业务需要在运行时进行装载、卸载操作组件的动态更新:组件在运行时可更新替换当前版本组件的版本隔离:不同组件引用相同产品的不同版本程序集可以版本隔离组件完整的生命周期:包括已安装、已装载、已激活、启动中、停止中、已卸载组件的加载顺序:组件加载根据业务要求可设置加载级别来控制加载次序组件的通信支持:组件间通过面向服务的编程模型来达到组件间通信、调用的目的组件的扩展支持:组件提供了扩展点及其扩展来满足某个组件的扩展性支持启动一个OSGi.NET应用程序仅需要如下代码创建一个OSGi.NET项目需要:引用框架内核程序集OSGi.NET.dll添加框架内核配置文件OSGi.NET.properties如需要日志支持,添加log4net.config文件及log4net.dll程序集引用OSGi.NET项目的默认文件目录结构如下/程序目录/程序目录/Bundles//程序目录/Bundles/模块A//程序目录/Bundles/模块A/Manifest.xml/程序目录/Bundles/模块A/模块A.dll/程序目录/Bundles/模块A/Libs//程序目录/Bundles/模块A/Libs/*.dll/程序目录/Bundles/模块B//程序目录/Bundles/模块C//程序目录/Libs/(可选)/程序目录/OSGi.NET.properties注:程序目录中的Libs文件夹存放个Bundles的共享程序集(也可通过在配置文件中配置共享清单),如接口契约、公共第三方类库等。模块A中的Libs文件夹存放其私有程序集。Manifest.xml作为程序清单文件对模块进行自描述。OSGi.NET.properties为框架内核配置文件关于加载次序:由于业务需求,各模块存在依赖关系的可能,所以模块加载也会有加载顺序的要求,此时可以通过清单文件中Manifest.xml,Bundle节点的StartLevel属性对其加载次序进行设置。数值越小,加载越前。关于Bundle引用程序集搜索原则:根据加载的Bundle引用程序集,依据程序集名称+版本号匹配原则,优先从[/程序目录/Libs/]目录或共享清单中搜索。如第一步无匹配,则根据程序集名称从[/程序目录/Bundles/模块A/Libs/*.dll]目录搜索,并将搜索到的程序集对应版本关联Bundle。各Bundle下Libs目录程序集在加载中做了Bundle间的隔离,确保不同的Bundle引用的程序集间不会造成影响。即:如存在共享程序集请放置[/程序目录/Libs/]目录或在共享清单配置。
-
C#开发基础的编程工具VS2013入门基础及使用技巧
安装MicrosoftVisualStudio2013略过,这节重点介绍如何更好的使用VS。C#即CSharp,C#编程语言基于微软.netFramework框架下开发。netFramework是一个平台,也是一个技术。Visualstudio(简称VS)是一个集成化开发平台环境,也叫IDE工具。a.新建项目,如图“文件->新建->项目”b.选择语言,如VisualC#;选择创建的应用程序类型;如WPF应用程序或者控制台应用程序,这里选择控制台应用程序类型,作为我们第一个程序列子;c.定义项目名称,如ConsoleApplication1d.位置,浏览选择项目代码的存放位置。e.解决方案,重点说一下解决方案这个选项有2种,一种创建新的解决方案,另外是添加到已有的解决方案。f.解决方案名称,自定义即可。解决方案管理器的使用,解决方案管理器视图,全局的显示当前项目的文档结构。一个解决方案,可以囊括多个项目。后面会对复杂情况多项目的配置进行介绍。编辑器设置及介绍a.行号的显示,“工具-》选项-》文本编辑器-》C#-》常规-》”勾选【行号】就可以显示代码的行号了。显示了行号,配合快捷键CTRL+G,就有利于快速跟进代码。b.主题皮肤调整,“工具-》选项-》环境-》颜色主题“,一种皮肤一种心情,随个人喜好调整。c.字体调整。“工具-》选项-》环境-》字体和颜色“。好的字体,可以区分数字0和字母o,数字1和字母l及其他容易视觉混淆的字。这里推荐编程用consolas字体,其他好的字体等待你自己去发掘吧。d.背景颜色调整。“工具-》选项-》环境-》字体和颜色-》项背景“。开发人员眼睛每天面对电脑超过8小时以上,好的背景色会对保护视力有好处。你可以根据自己喜欢,设置成任何颜色,这里已视力保护色(色调85,饱和度123,亮度205)为例子截图。新手需对【工具-选项】的设置项多摸索,多尝试,才能对工具得心应手。写C#第一个程序,helloworld。程序功能:在CMD窗口命令中输出字符,"helloworld"a.在刚才新建的编辑器界面,在程序入口main函数中输入代码,如图Console.WriteLine("helloworld");这句是在控制台打印helloworld字符Console.ReadKey();这句是控制台等待键盘录入。b.生成解决方案【生成】-》【生成解决方案】,快捷键按F6。c.启动调试【调试】-》【启迪调试】,快捷键按F5。会弹出黑色背景CMD窗口。显示helloworld,即成功。自豪吧。新手简单错误调试和Debug,IDE已经非常智能了,错误都会给出相应的提示。错误分为两种语法错误和运行错误,新手阶段首先保证没有语法错误。生成的时候程序报错,新手不要害怕,耐心仔细看下错误列表的错误提示;如图13行,缺少分号;。在行末尾加上分号,重新F6生成错误就好消失,F5运行成功。vs2013快捷键a.选择整行,光标移至该行,组合键“Ctrl+C”即可,而无需选择整行。b.光标移至该行,组合键“Ctrl+X”来完成剪切整行操作。c.光标移至该行,组合键“Ctrl+L”来完成删除整行操作。d.使用组合键“Ctrl+I”,搜索。e.组合键“Shift+Alt+方向键(或鼠标)”即可完成框式选择。f.home键:定位到当前行的行首;end键:定位到当前行的行尾。g.调用参数信息提示,将光标置于参数名上,再按组合键“Ctrl+Shif+空格"。h.调用智能提示1)方法1:使用组合键“Ctrl+J”;2)方法2:使用组合键“Alt+→”。i.调试相关1)调试(启动):F5;2)调试(重新启动):使用组合键“Ctrl+Shift+F5”;3)调试(开始执行不调试):使用组合键“Ctrl+F5”;4)调试(逐语句):F11;5)调试(逐过程):F10;6)设置断点:F9。复杂情况,一个解决方案下创建多个项目的编译与运行。1.选中解决方案,右键-》添加项目,即可创建多个项目,如下图。2.在【文件】-》【新建项目】,在解决方案中选择【添加到解决方案】,可以看到解决方案视图中,有2个项目。3、缺点,在一个解决方案中存在多个项目情况时,每次生成解决方案时(F6)会把解决方案中的每个项目代码重新生成,如果项目较大代码较多,每次生成会比较耗时。4、【设为启动项目】多项目情况下,只有【默认启动项目】才会被调试运行,如果要切换启动项目,可以选中该项目,邮件,设为启动项目即可,如下图。复杂情况,项目的加载与卸载。1、如上面8步骤中有提到,实际在一个解决方案中存在多个子项目时,每次生成全部项目代码,运行耗时很久。这里介绍项目的卸载操作,将不需要的项目暂时从解决方案中卸载,这里不是删除哦,实际项目代码文件都在,后面还可以重新通过加载操作,导入解决方案中来。如下图卸载和加载操作。
-
【python】python3 下的元类窥探及实现
【一、了解元类】究竟为什么要使用元类?究竟是为什么你会去使用这样一种容易出错且晦涩的特性?好吧,一般来说,你根本就用不上它:“元类就是深度的魔法,99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。”——Python界的领袖TimPeters【PS:假设元类:是一根竹子,如果不告诉你最会竹子要做成筷子,你在不知道用在什么地方的情况,你是越研究,越迷糊的。SO,我们从第二篇开始,从小例子开始,慢慢了解怎么做。】以下内容:看得懂的看,看不懂的,可以直接跳过看最后的结论,记住最后结论,再看后续篇幅:但就元类本身而言,它们其实是很简单的:拦截类的创建修改类返回修改之后的类元类1.类也是对象在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段。在Python中这一点仍然成立:>>>classObjectCreator(object):…pass…>>>my_object=ObjectCreator()>>>print(my_object)但是,Python中的类还远不止如此。类同样也是一种对象。是的,没错,就是对象。只要你使用关键字class,Python解释器在执行的时候就会创建一个对象。下面的代码段:>>>classObjectCreator(object):…pass…将在内存中创建一个对象,名字就是ObjectCreator。这个对象(类对象ObjectCreator)拥有创建对象(实例对象)的能力。但是,它的本质仍然是一个对象,于是乎你可以对它做如下的操作:你可以将它赋值给一个变量你可以拷贝它你可以为它增加属性你可以将它作为函数参数进行传递下面是示例:>>>print(ObjectCreator)#你可以打印一个类,因为它其实也是一个对象>>>defecho(o):…print(o)…>>>echo(ObjectCreator)#你可以将类做为参数传给函数>>>print(hasattr(ObjectCreator,'new_attribute'))Fasle>>>ObjectCreator.new_attribute='foo'#你可以为类增加属性>>>print(hasattr(ObjectCreator,'new_attribute'))True>>>print(ObjectCreator.new_attribute)foo>>>ObjectCreatorMirror=ObjectCreator#你可以将类赋值给一个变量>>>print(ObjectCreatorMirror())2.动态地创建类因为类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样。首先,你可以在函数中创建类,使用class关键字即可。>>>defchoose_class(name):…ifname=='foo':…classFoo(object):…pass…returnFoo#返回的是类,不是类的实例…else:…classBar(object):…pass…returnBar…>>>MyClass=choose_class('foo')>>>print(MyClass)#函数返回的是类,不是类的实例>>>print(MyClass())#你可以通过这个类创建类实例,也就是对象但这还不够动态,因为你仍然需要自己编写整个类的代码。由于类也是对象,所以它们必须是通过什么东西来生成的才对。当你使用class关键字时,Python解释器自动创建这个对象。但就和Python中的大多数事情一样,Python仍然提供给你手动处理的方法。还记得内建函数type吗?这个古老但强大的函数能够让你知道一个对象的类型是什么,就像这样:>>>print(type(1))#数值的类型>>>print(type("1"))#字符串的类型>>>print(type(ObjectCreator()))#实例对象的类型>>>print(type(ObjectCreator))#类的类型仔细观察上面的运行结果,发现使用type对ObjectCreator查看类型是,答案为type,是不是有些惊讶。。。看下面3.使用type创建类type还有一种完全不同的功能,动态的创建类。type可以接受一个类的描述作为参数,然后返回一个类。(要知道,根据传入参数的不同,同一个函数拥有两种完全不同的用法是一件很傻的事情,但这在Python中是为了保持向后兼容性)type可以像这样工作:type(类名,由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))比如下面的代码:In[2]:classTest:#定义了一个Test类...:pass...:In[3]:Test()#创建了一个Test类的实例对象Out[3]:可以手动像这样创建:Test2=type("Test2",(),{})#定了一个Test2类In[5]:Test2()#创建了一个Test2类的实例对象Out[5]:我们使用"Test2"作为类名,并且也可以把它当做一个变量来作为类的引用。类和变量是不同的,这里没有任何理由把事情弄的复杂。即type函数中第1个实参,也可以叫做其他的名字,这个名字表示类的名字In[23]:MyDogClass=type('MyDog',(),{})In[24]:print(MyDogClass)使用help来测试这2个类In[10]:help(Test)#用help查看Test类HelponclassTestinmodule__main__:classTest(builtins.object)|Datadescriptorsdefinedhere:||__dict__|dictionaryforinstancevariables(ifdefined)||__weakref__|listofweakreferencestotheobject(ifdefined)In[8]:help(Test2)#用help查看Test2类HelponclassTest2inmodule__main__:classTest2(builtins.object)|Datadescriptorsdefinedhere:||__dict__|dictionaryforinstancevariables(ifdefined)||__weakref__|listofweakreferencestotheobject(ifdefined)4.使用type创建带有属性的类type接受一个字典来为类定义属性,因此>>>Foo=type('Foo',(),{'bar':True})可以翻译为:>>>classFoo(object):…bar=True并且可以将Foo当成一个普通的类一样使用:>>>print(Foo)>>>print(Foo.bar)True>>>f=Foo()>>>print(f)>>>print(f.bar)True当然,你可以继承这个类,代码如下:>>>classFooChild(Foo):…pass就可以写成:>>>FooChild=type('FooChild',(Foo,),{})>>>print(FooChild)>>>print(FooChild.bar)#bar属性是由Foo继承而来True注意:type的第2个参数,元组中是父类的名字,而不是字符串添加的属性是类属性,并不是实例属性5.使用type创建带有方法的类最终你会希望为你的类增加方法。只需要定义一个有着恰当签名的函数并将其作为属性赋值就可以了。添加实例方法In[46]:defecho_bar(self):#定义了一个普通的函数...:print(self.bar)...:In[47]:FooChild=type('FooChild',(Foo,),{'echo_bar':echo_bar})#让FooChild类中的echo_bar属性,指向了上面定义的函数In[48]:hasattr(Foo,'echo_bar')#判断Foo类中是否有echo_bar这个属性Out[48]:FalseIn[49]:In[49]:hasattr(FooChild,'echo_bar')#判断FooChild类中是否有echo_bar这个属性Out[49]:TrueIn[50]:my_foo=FooChild()In[51]:my_foo.echo_bar()True添加静态方法In[36]:@staticmethod...:deftest_static():...:print("staticmethod....")...:In[37]:Foochild=type('Foochild',(Foo,),{"echo_bar":echo_bar,"test_static":test_static})In[38]:fooclid=Foochild()In[39]:fooclid.test_staticOut[39]:In[40]:fooclid.test_static()staticmethod....In[41]:fooclid.echo_bar()True添加类方法In[42]:@classmethod...:deftest_class(cls):...:print(cls.bar)...:In[43]:In[43]:Foochild=type('Foochild',(Foo,),{"echo_bar":echo_bar,"test_static":test_static,"test_class":test_class})In[44]:In[44]:fooclid=Foochild()In[45]:fooclid.test_class()True你可以看到,在Python中,类也是对象,你可以动态的创建类。这就是当你使用关键字class时Python在幕后做的事情,而这就是通过元类来实现的。较为完整的使用type创建类的方式:classA(object):num=100defprint_b(self):print(self.num)@staticmethoddefprint_static():print("----haha-----")@classmethoddefprint_class(cls):print(cls.num)B=type("B",(A,),{"print_b":print_b,"print_static":print_static,"print_class":print_class})b=B()b.print_b()b.print_static()b.print_class()#结果#100#----haha-----#1006.到底什么是元类(终于到主题了)元类就是用来创建类的“东西”。你创建类就是为了创建类的实例对象,不是吗?但是我们已经学习到了Python中的类也是对象。元类就是用来创建这些类(对象)的,元类就是类的类,你可以这样理解为:MyClass=MetaClass()#使用元类创建出一个对象,这个对象称为“类”my_object=MyClass()#使用“类”来创建出实例对象你已经看到了type可以让你像这样做:MyClass=type('MyClass',(),{})这是因为函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。现在你想知道那为什么type会全部采用小写形式而不是Type呢?好吧,我猜这是为了和str保持一致性,str是用来创建字符串对象的类,而int是用来创建整数对象的类。type就是创建类对象的类。你可以通过检查__class__属性来看到这一点。Python中所有的东西,注意,我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type。>>>age=35>>>age.__class__>>>>>>name='bob'>>>name.__class__>>>>>>deffoo():pass>>>foo.__class__>>>>>>classBar(object):pass>>>b=Bar()>>>b.__class__>>>现在,对于任何一个__class__的__class__属性又是什么呢?>>>a.__class__.__class__>>>age.__class__.__class__>>>foo.__class__.__class__>>>b.__class__.__class__因此,元类就是创建类这种对象的东西。type就是Python的内建元类,当然了,你也可以创建自己的元类。7.__metaclass__属性你可以在定义一个类的时候为其添加__metaclass__属性。classFoo(object):__metaclass__=something…...省略...如果你这么做了,Python就会用元类来创建类Foo。小心点,这里面有些技巧。你首先写下classFoo(object),但是类Foo还没有在内存中创建。Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的type来创建这个类。把下面这段话反复读几次。当你写如下代码时:classFoo(Bar):passPython做了如下的操作:Foo中有__metaclass__这个属性吗?如果是,Python会通过__metaclass__创建一个名字为Foo的类(对象)如果Python没有找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。现在的问题就是,你可以在__metaclass__中放置些什么代码呢?答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化type的东东都可以。8.自定义元类元类的主要目的就是为了当创建类时能够自动地改变类。假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到,但其中一种就是通过在模块级别设定__metaclass__。采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。幸运的是,__metaclass__实际上可以被任意调用,它并不需要是一个正式的类。所以,我们这里就先以一个简单的函数作为例子开始。python2中#-*-coding:utf-8-*-defupper_attr(class_name,class_parents,class_attr):#class_name会保存类的名字Foo#class_parents会保存类的父类object#class_attr会以字典的方式保存所有的类属性#遍历属性字典,把不是__开头的属性名字变为大写new_attr={}forname,valueinclass_attr.items():ifnotname.startswith("__"):new_attr[name.upper()]=value#调用type来创建一个类returntype(class_name,class_parents,new_attr)classFoo(object):__metaclass__=upper_attr#设置Foo类的元类为upper_attrbar='bip'print(hasattr(Foo,'bar'))print(hasattr(Foo,'BAR'))f=Foo()print(f.BAR)python3中#-*-coding:utf-8-*-defupper_attr(class_name,class_parents,class_attr):#遍历属性字典,把不是__开头的属性名字变为大写new_attr={}forname,valueinclass_attr.items():ifnotname.startswith("__"):new_attr[name.upper()]=value#调用type来创建一个类returntype(class_name,class_parents,new_attr)classFoo(object,metaclass=upper_attr):bar='bip'print(hasattr(Foo,'bar'))print(hasattr(Foo,'BAR'))f=Foo()print(f.BAR)现在让我们再做一次,这一次用一个真正的class来当做元类。#coding=utf-8classUpperAttrMetaClass(type):#__new__是在__init__之前被调用的特殊方法#__new__是用来创建对象并返回之的方法#而__init__只是用来将传入的参数初始化给对象#你很少用到__new__,除非你希望能够控制对象的创建#这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__#如果你希望的话,你也可以在__init__中做些事情#还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用def__new__(cls,class_name,class_parents,class_attr):#遍历属性字典,把不是__开头的属性名字变为大写new_attr={}forname,valueinclass_attr.items():ifnotname.startswith("__"):new_attr[name.upper()]=value#方法1:通过'type'来做类对象的创建returntype(class_name,class_parents,new_attr)#方法2:复用type.__new__方法#这就是基本的OOP编程,没什么魔法#returntype.__new__(cls,class_name,class_parents,new_attr)#python3的用法classFoo(object,metaclass=UpperAttrMetaClass):bar='bip'#python2的用法#classFoo(object):#__metaclass__=UpperAttrMetaClass#bar='bip'print(hasattr(Foo,'bar'))#输出:Falseprint(hasattr(Foo,'BAR'))#输出:Truef=Foo()print(f.BAR)#输出:'bip'就是这样,除此之外,关于元类真的没有别的可说的了。但就元类本身而言,它们其实是很简单的:拦截类的创建修改类返回修改之后的类
-
C/C++程序内存分配
C/C++编译程序内存分配动态存储区栈区局部变量区,由编译器自动分配释放,存放函数的参数值,局部变量的值等。堆区动态存储区,malloc申请的变量,手动申请,手动释放。使用malloc或者new进行堆的申请,堆的总大小为机器的虚拟内存的大小。new和malloc的区别如下:(1)malloc是C语言中的函数,而new是C++中的操作符。(2)malloc申请之后返回的类型是void*,而new返回的指针带有类型。(3)malloc只负责内存的分配而不会调用类的构造函数,而new不仅会分配内存,而且会自动调用类的构造函数。静态存储区静态存储区内变量在程序编译阶段已经分配好内存空间并初始化。这块内存在程序的整个运行期间都存在,它主要存放静态变量(static)、全局变量(global)和常量。Tips:(1)静态存储区内不存在未初始化的变量,编译器会以默认的方式进行初始化。(2)静态存储区内的常量分为常变量和字符串常量,一经初始化,不可修改。静态存储内的常变量是全局变量,与局部常变量不同,区别在于局部常变量存放于栈,实际可间接通过指针或者引用进行修改,而全局常变量存放于静态常量区则不可以间接修改。(3)字符串常量存储在静态存储区的常量区,字符串常量的名称即为它本身,属于常变量。代码区存放程序体的二进制代码。参考网上的示例代码inta=0;//全局初始化区char*p1;//全局未初始化区main(){intb;栈chars[]="abc";//栈char*p2;//栈char*p3="123456";//1234560在常量区,p3在栈上。staticintc=0;//全局(静态)初始化区p1=(char*)malloc(10);p2=(char*)malloc(20);//分配得来得10和20字节的区域就在堆区。strcpy(p1,"123456");//1234560放在常量区,编译器可能会将它与p3所向"123456"优化成一个地方。}1234567891011121314栈&堆申请效率比较栈:由系统自动分配,速度较快。但程序员是无法控制的。堆:是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,但便于使用。
-
C++学习笔记(this指针)
首先,要理解什么是指针?简单的说指针就是地址。例如inta=3;定义整型变量a,并给a赋初值为3。在定义a的时候,系统会为变量a分配内存,内存是有地址的,就像每个房间都有自己的房间号一样。这里我们假设a的地址为0xf0,接着定义指针变量int*p=&a;表明指针变量p存放的是a的地址(0xf0)。因此,指针变量就是用来存放地址的变量。在C++中提供了类,假设定义了如下的类:#includeusingnamespacestd;classTest{public:Test(intd)//构造函数{data=d;}voidshow()//成员函数{cout
-
JSP内置对象/EL/JSTL
一:MVC设计模式MVC是一个设计模式,它强制的使应用层的输入,处理和输出分开。使用MVC设计模式被分为三个核心层:模型层,视图层,控制层。它们各自处理自己的任务。显示层(View):此层主要是负责将内容显式给用户。比如:JSP控制层(Controller):负责判断所有的用户请求参数是否合法,根据请求的类型调用模型层执行操作,再讲处理结果交给显示层显示。eg:servlet模型层(Model):操作数据库的独立的操作组件,或使用lavaBean(POJO)保存数据。二:JSP内置对象JSP中提供了九个内置对象,这些内置对象由容器为用户进行实例化,用户直接使用即可。eg:[html]viewplaincopy(1):>response内置对象title>head>3秒之后跳转到index页面h3>body>html>[html]viewplaincopy(2):>index.jsptitle>head>跳转过来了h3>body>html>[html]viewplaincopy(3):>处理错误的页面title>head>body>html>[html]viewplaincopy(4):JSPProjectdisplay-name>index.htmlwelcome-file>index.htmwelcome-file>index.jspwelcome-file>default.htmlwelcome-file>default.htmwelcome-file>default.jspwelcome-file>welcome-file-list>ctxparam-name>ContextParamValueparam-value>context-param>http://www.mycompany.comtaglib-uri>/WEB-INF/c.tldtaglib-location>taglib>jsp-config>web-app>三:EL表达式语言使用表达式语言(EL)可以在JSP页面进行方便的输出内容:EL语法:${表达式}EL有自己的内置对象:PageContext,pageScope,requestScope,sessionScope,applicationScope,param,paramValues,header,headerValues,cookie,initParam1.使用EL访问不同的属性范围:${pageScope.属性名},${requestScope.属性名},${sessionScope.属性名},${applicationScope.属性名},这四种属性访问范围由小到大。Eg:[html]viewplaincopy>EL访问不同属性范围的值title>head>page===>${pageScope.info}h3>request===>${requestScope.info}h3>session===>${sessionScope.info}h3>application===>${applicationScope.info}h3>${info}=====>${info}h3>body>html>2.EL访问JSP内置对象使用EL的pageContext内置对象访问JSP的内置对象:${pageContext.对应的jsp内置对象}Eg:EL获取上下文路径:特殊:${pageContext.servletContext.contextPath}EL访问session的ID:${pageContext.session.id}Eg::[html]viewplaincopy>EL访问jsp内置对象title>head>直接通过JSP内置对象获取上下文路径:h3>通过EL获取上下文路径:${pageContext.servletContext.contextPath}h3>通过EL获取sessionID:${pageContext.session.id}h3>通过EL判断当前的请求方式:${pageContext.request.method}h3>body>html>3.EL访问参数(访问客户端发送的参数。全局参数,一组参数)用途1:使用EL的param内置对象访问客户端发送的参数${param.参数名}eg:[html]viewplaincopy>EL访问客户端发送的请求参数title>head>客户端发送来的username参数是:${param.username}h3>客户端发送来的pwd参数是:${param.pwd}h3>body>html>用途2:使用EL的initParam内置对象访问上下文参数(全局参数),在web.xml中配置上下文参数:adminObamaeg:[html]viewplaincopy>EL访问全局参数(上下文参数)title>head>全局参数为ctx的值是:${initParam.ctx}h3>body>html>用途3:使用EL的paramValues内置对象访问一组参数${paramValues.参数名[0]}访问一组参数值${paramValues.参数名[n]}访问n+1组参数值eg:[html]viewplaincopy>兴趣爱好页面title>head>运动:电影:读书:音乐:form>body>html>[html]viewplaincopy>EL访问一组参数title>head>爱好是:h3>${paramValues.hobby[0]}h3>${paramValues.hobby[1]}h3>${paramValues.hobby[2]}h3>${paramValues.hobby[3]}h3>body>html>4.访问cookie通过EL的cookie内置对象访问JSESSIONID名称的cookie的语法:${cookie[“JSESSIONID”].name},${cookie[“JSESSIONID”].value}[html]viewplaincopy>EL的cookie内置对象访问Cookietitle>head>Cookie名为JSESSIONID的值为:${cookie["JSESSIONID"].value}h3>sessionID为:${pageContext.session.id}h3>body>html>5.访问header${header[“cookie”]}EL的运算符1.算数运算符+-*/(div)%(mod)eg:${param.num1+param.num2}参数相加2.关系运算符<(lt)>(gt)==(eq)!=(ne)>=(ge)EL运算符title>head>num1+num2=${param.num1+param.num2}h3>num1/num2=${param.num1/param.num2}也可以div${param.num1divparam.num2}h3>num1是否等于num2:${param.num1==param.num2}h3>true&&true:${true&&true}h3>true||false:${true||false}h3>${emptyparam.abc}h3>三目运算符:${param.num1>param.num2?"num1真的大于num2":"num1不大于num2"}h3>body>html>四:JSTL(JSPStandardTagLibraries)引入JSTL的步骤:第一步:将jstl-xx.jar导入/WEB-INF/lib文件夹下。第二步:将jstl-xx.jar解压后的META-INF文件夹下的xxx.tld文件拷贝到/WEB-INF/的某目录下第三步:在JSP页面使用taglib指令引入xxx.tld文件。JSP页面使用taglib指令引入xxx.tld文件注意:uri中指向xxx.tld文件的路径有两种写法:第一种:直接指向xxx.tld文件的路径第二种:在web.xml中配置taglib的urihttp://www.xxx.com/WEB-INF/c.tld1.输出标签2.设置标签设置属性范围的属性值设置对象的属性值3.捕获异常标签//有可能发生异常的代码4.判断标签//判断结果为true,执行此处5.forEach标签(遍历list集合或Map集合)${msg}6.choose标签=90}">优秀=80}">良好不及格,要加油!