首页 文章 精选 留言 我的

精选列表

搜索[知识管理],共10000篇文章
优秀的个人博客,低调大师

Python Flask学习知识点(六)

timg (10).jpg image.png Flask多线程机制 发送请求和服务器处理请求的线程之间的关系? 客户端发送十个请求,Flask开启多少个线程处理请求?其实是由web服务器开启的多线程。 如果要上生产线,一般不会用Flask自带的web server。 对于一个web网站而言,必须有承受并发的能力。 单线程: 如果是单线程,只有当一个请求处理完成之后,才会处理下一个请求,所以这时候看起来就像是要排队一样 单线程下,当一个请求进来之后,Flask会实例化一个Request对象,用这个对象来装载请求信息,这时候只有一个实例化的request,当第一个请求结束后,Flask会再实例化一个Request对象来装载第二个请求,然后request变量又指向当前请求,那么这样众多用户就不会混乱。 多线程: werkzeug库下local模块下的Local对象,Flask就是用Local来做线程隔离的。 Local对象的本质就是用线程的id号作为字典的键这种基本原理实现的。 Flask中线程隔离栈LocalStack LocalStack 是实现线程隔离的栈结构,把Local封装了。 LocalStack需要使用push方法 LocalStack: Local特性 Stack特性:栈结构,只能取栈顶元素,先进后出 使用线程隔离的意义在于:使当前线程能够正确引用到他自己做创建的对象,而不是引用到其他线程做创建的对象 线程隔离对象 LocalStack 问:Flask使用LocalStack为了隔离哪个对象? 答:被线程隔离对象 RequestContext AppContext Flask核心对象全局只有一个。 看一段测试代码,测试下LocalStack线程隔离: import threading from werkzeug.local import LocalStack stack = LocalStack() stack.push('a') print('主线程top:', stack.top) def child(): print("子线程:", stack.top) stack.push('b') print('子线程:', stack.top) t = threading.Thread(target=child, name='demo') t.start() print('主线程top:', stack.top) 打印结果: 主线程top: a 子线程: None 子线程: b 主线程top: a 还有哪些是被线程隔离的对象? image.png 以上都是被线程隔离的对象。 ViewModel的基本概念 很多时候,我们的数据在提交给页面的时候需要做一些处理或者裁剪,这时就需要ViewModel层来做这件事,如图: image.png viewModel作用 裁剪 修饰 合并 app文件夹下新建文件夹view_models,这里就可以处理数据的裁剪、修饰、合并。 单页面与网站的区别 一个网站页面由哪几部分构成? JS CSS(静态文件) HTML模板 数据 网站 一般来讲,服务器端把数据返回到HTML模板中,称之为服务器渲染,然后返回到浏览器给用户,此时,客户端浏览器网页中的JS CSS起作用再一次发起请求渲染页面,这是一个完整的页面就处理完了。 image.png 单页面 浏览器去请求一个静态的html,html返回到客户端浏览器后,html里边的js css 发起请求,来填充数据。 image.png 单页面和普通网站有什么区别: 区别的第一种说法: 对于多页面普通网站而言,大多数情况下它的数据渲染或者说模板填充都是在服务器端进行的, 而对于单页面来说,他数据的渲染是在客户端进行的。 区别的第二种说法: 单页面业务逻辑也就是说数据运算主要集中在客户端用js去操作,而普通网站而言,绝大多数业务逻辑是在服务器,也就是视图函数中去进行的。

优秀的个人博客,低调大师

Python Flask学习知识点(四)

timg (8).jpg 上节讲到,视图函数可以以接受参数的形式来获取传入后台的参数,但是往往我们需要对参数进行校验,比如说请求一个10条数据,那么page=10,但是如果某些比较皮的小盆友传入一个page=10000,那服务器也要去数据库查询10000条记录返回吗?显然不能那么做,所以这就涉及到在Flask中做参数检查的工作。 WTForms参数验证 首先,安装这个第三方的插件wtformspip install wtforms 我们都知道,在web应用中,分层这个概念非常重要,MVC模式其实可以理解为就是分层,这里我们引入验证层这个概念。无论写任何的web应用,验证层都是非常重要的概念。 所以我们所有的参数校验工作,都不会直接写到视图函数中,而是都会放到验证层去做。 在app文件夹下新建forms文件夹,并新建book.py文件: image.png 编写book.py: from wtforms.validators import Length, NumberRange, DataRequired() from wtforms import Form, StringField, IntegerField class SearchForm(Form): q = StringField(validators=[DataRequired(), Length(min=1, max=30)]) page = IntegerField(validators=[NumberRange(min=1, max=99)], default=1) 解释这段代码: 使用wtforms提供的StringField对象来定义参数q的类变量 使用wtforms提供的IntegerField对象来定义参数page的类变量 wtforms内置了很多的验证对象来帮助我们快速的完成对参数的验证,而不需要手动编写验证函数。当然了,我们也可以自定义验证对象。 为了验证q参数,可以使用内置的验证对象:Length,验证q参数的长度,validators接受一个list,可以传入多个验证对象,这里我们验证q参数传入Length验证对象。 同样,验证page参数也是同样的原理,这里使用的三个内置的验证对象(NumberRange,Length,DataRequired),可以查阅文档了解详细的用法。 编写完验证层代码,在视图函数中调用,更改web文件夹下book.py: from flask import jsonify, request from flaskDemo.app.forms.book import SearchForm from . import web @web.route('/hello') def search(): form = SearchForm(request.args) if form.validate(): q = form.q.data page = form.page.data result = {"name": q, "valus": page} return jsonify(result) return jsonify({"error": 0}) 解释代码: 首先导入SearchForm并实例化为form对象,并且传入参数request.args,通过form.validate()的返回值判断是否符合验证器规定的结果。 这里讲一点,使用page = form.q.data这种方式获取q和page的值,而不直接使用q,是因为我们之前在验证层SearchForm中定义了page参数如果为空,使用1为默认值,所以我们要从form.q.data 这种拿到page参数。 看下效果,运行run.py启动,在浏览器中输入符合规则的urlhttp://127.0.0.1:8000/hello?q=demo&page=1 再输入不符合规则的urlhttp://127.0.0.1:8000/hello?q=demo&page=100 返回结果: image.png image.png 一个是成功,一个是失败。 因为之前我们在验证层中定义了page的最大值为99,所以如果输入100,就会返回错误。 再试一下不输入page参数:http://127.0.0.1:8000/hello?q=demo image.png 还是可以返回默认值。 继续,看下图: image.png debug调试过程中,看下form实例中的一个errors,这个errors就是当验证不通过时,wtforms给我们的一个错误提示,在一般情况下,如果参数验证不通过时是会抛出一个异常的,但是使用wtforms,它会把错误提示放到errors属性中而不抛出异常。 更改代码: def search(): form = SearchForm(request.args) if form.validate(): q = form.q.data page = form.page.data result = {"name": q, "valus": page} return jsonify(result) return jsonify({"error": form.errors}) 这里改为return jsonify({"error": form.errors})。 再运行代码输入URLhttp://127.0.0.1:8000/hello?q=demo&page=100得到如下: image.png 可见wtforms告诉我们为什么验证不通过。 这里也可以在SearchForm自定义返回的错误信息,只需更改为: class SearchForm(Form): q = StringField(validators=[DataRequired(), Length(min=1, max=30)]) page = IntegerField(validators=[NumberRange(min=1, max=99, message="传入的参数不符合要求")], default=1) 再运行代码: image.png 拆分配置文件 之前我们一直是把配置参数全部放到config.py中,但是这样的话会有一个问题,那就是如果我们把比较私密的配置(例如数据库地址以及密码)和普通的参数放在一起,势必会有安全风险,比如把代码传到了git上,所以最好把配置文件拆分为两种不同级别的,这里我们把config.py拆分为secure.py和setting.py,并且全部放到app文件夹下。 secure.py 用来存放数据库密码、账号还有我们后边提到的flask app key ,这样比较机密的信息,单独放配置文件中,还有就是开发环境和生产环境中的不同设置,比如 debug=True。 setting.py 用来存放不涉及到机密的,生产和开发环境一样的配置。 更改之前的代码,app文件夹下的__init__.py: def create_app(): app = Flask(__name__) app.config.from_object("app.setting") app.config.from_object("app.secure") register_blueprint(app) return app current_app 在Flask中,如果要在运行中读取配置文件中的参数,需要使用Flask核心对象app来查找,但是之前讲过,如果反复导入app,或造成循环导入,那么解决方法就是使用current_app,current_app其实就是指代的app, 例如,读取配置文件中的PER_PAGE变量: 因为之前我们做了这个操作: def create_app(): app = Flask(__name__) app.config.from_object("app.setting") app.config.from_object("app.secure") register_blueprint(app) return app 把setting.py和secure.py中的配置加到了Flask内置config对象中,所以要这样读取配置文件中的变量: current_app.config['PER_PAGE'] 欲知后事如何,请看下回分解,记得点个赞~感谢

优秀的个人博客,低调大师

JVM知识点扫盲系列(1)

每次young gc的时间,和eden空间的大小是正比关系吗? 在进入公司之后,这个问题先后被多次问到,那young gc的时间到底和哪些因素有关呢? 来看一段代码,逻辑很简单,不断的分配1M的大小,直到触发YGC。 // -Xmx2g -Xms2g -Xmn500m -XX:+PrintGCDetails // -XX:+UseConcMarkSweepGC -XX:+PrintHeapAtGC public class GcCase { public static void main(String[] args) { for (int i = 0; i < 1000; i++) { allocate_1M(); } } public static void allocate_1M() { byte[] _1M = new byte[1024 * 1000]; } } 这里设置了新生代的大小是500m,按照8:1:1的比例,eden的大小应该是400m。我们可以大概梳理一下: 1、每次分配1M,分配到第400次时,eden被1M的内存块塞满了 2、为了能够塞下后面的数据的,只能触发一次YGC 3、执行YGC之前,必须等所有的业务线程全部挂起,这个点就是所谓的安全点(safepoint),从这一刻开始,JVM是冻结的,只有虚拟机线程才能执行,也是从这一刻开始计时。 一次YGC过程大概包括下面几个步骤,每个步骤的耗时之和,也就是整个YGC的耗时。 1、找出所有可能存活的对象,一个也不能少,这是标记过程的耗时。 从GC Roots开始,遍历对象,所有能遍历到的对象都是算是存活对象,打上一个标记。正常情况下,需要的关心的GC Roots包括下面几个:类的静态引用、业务线程进入安全点时,正在执行方法中的引用类型变量(包含参数)、部分老年代的对象(持有引用执行新生代对象),还有其它不需要太关注的,比如VM内部的数据结构。 2、存活对象被标记出来之后,需要把这些对象从eden区或者from区,复制到to区,这个复制过程的耗时,和存活对象的大小数量有很大的关系。这是复制过程的耗时。 3、如果开启了GC日志-XX:+PrintGCDetails,那么在YGC过程中,还会记录一些数据到日志中,这个日志的写入过程,也有可能被影响,这段时间也算YGC的耗时。 所以,young gc的时间,和eden空间的大小是正比关系吗? 答案是没有多大关系。 欢迎工作一到五年的Java工程师朋友们加入Java架构开发:744677563 本群提供免费的学习指导 架构资料 以及免费的解答 不懂得问题都可以在本群提出来 之后还会有职业生涯规划以及面试指导

优秀的个人博客,低调大师

Spring Boot零散知识总结

一、@Value与@ConfigurationProperties @Value 与 @ConfigurationProperties 都可以用于配置文件与 JavaBean 中的属性映射,主要有以下区别 对比项@ConfigurationProperties@Value作用批量注入配置文件中的属性单个指定配置文件中的属性松散语法(userName->user-name)支持不支持SpEl表达式不支持支持JSR303数据校验支持不支持复杂的数据类型(map)支持不支持 @ConfigurationProperties:一般用于配置文件与 JavaBean 的映射 @Value:一般用于与某个值的映射 二、@ImportResource与@PropertySource @ImportResource:因为 Spring Boot 项目默认是没有 Spring 配置文件的,可以使用这个注解引入自定义的 Spring 配置文件。 注意:加在入口类上才会生效,例如:@ImportResource(locations = {"classpath:spring-bean.xml"})。因为 Spring Boot 本身就是为了简化配置的,因此一般使用注解的形式来代替这种配置文件的形式,来注入 Bean。 @PropertySource:指定全局配置文件的路径,从而完成配置文件与 JavaBean 属性的映射。例如:@PropertySource(value = {"classpath:person.properties"})。 三、Spring Boot 全局配置文件路径设置 Spring boot 默认会扫描 application.properties 或者 application.yml 配置文件,自动扫描的路径有以下 4 个: 按照优先级排序为:file/config/,file/,classpath/config/,classpath/,高优先级会覆盖低优先级的配置文件。采用的覆盖非失效覆盖,而是通过覆盖实现互补配置。 项目发布的时候,还可以通过命令参数 spring.config.location= 设置配置文件的路径。在项目启动时候行动态的设置。 欢迎加入Java高级架构学习交流群:375989619 本群提供免费的学习指导 架构资料 以及免费的解答 不懂得问题都可以在本群提出来 之后还会有职业生涯规划以及面试指导 进群修改群备注:开发年限-地区-经验 方便架构师解答问题 免费领取架构师全套视频!!!!!!!!

优秀的个人博客,低调大师

Python函数、对象几个知识点

函数 一、参数 Python的函数参数可以定义默认值,当调用者没有提供参数时,函数在执行过程中用默认值设置该参数。 有默认值的参数必须声明在没有默认值的参数之后,例: def sum(x, y = 10): return x + y 除了按参数的声明顺序传递参数,还可以不按顺序传递命名参数,例: def sum(x, y, z) return x+y+z if __name__ == "__main__": #如下两种调用方式意义相同 sum(1,2,3) sum(z = 3, y = 2 ,x = 1) 二、变长参数 变长参数的函数:参数个数可变,类型不定的函数。 元组变长参数(*args):适用于参数数量不固定,在函数中使用这些参数时无需知道参数的名字与场合。 字典变长参数(**args):适用于参数数量不固定,在函数时使用这些参数时需要知道参数名字与场合。 #元组变长参数 def demo(message, *tupleName): for name in tupleName: print(message,name) if __name__ == "__main__": demo("Good morning","A","B","C","D") 运行结果: Good morning A Good morning B Good morning C Good morning D #字典变长参数 def check_book(**kwargs): print([k for k in kwargs]) c_dict = {'A':'jianshu', 'C':'简书', 'price':1000} check_book(**c_dict) 运行结果: ['A', 'price', 'C'] 对象 一、类(class):类定义了事物的属性和它可以做到的行为。 类中 成员变量 = 属性 类中 成员函数 = 方法 二、对象(object):是类的实例。 对象指的是类的实例 类是创建对象的模板 创建对象的过程也叫做类的实例化 class MyClass(object): """ message:成员变量 赋初值 show(self):成员函数,必须要带参数self """ message = "Hello World" def show(self): print(self.message) 参数self是对象本身的引用,在成员函数体中可以引用self参数获得对象信息. 通过在类名后边加小括号可以直接实例化类来获得对象变量,使用对象变量可以访问累的成员函数和成员变量。 重点: Python中直接在类作用域中定义的成员变量相当于C、C++中的静态成员变量,即可以通过类名访问MyClass.message,也可以通过对象访问inst = MyClass(), inst.message,因此,类和所有该类的对象共享同一个成员变量。 三、构造函数 构造函数是一种特殊的类成员方法,主要用来创建对象时初始化对象,即为对象的成员变量赋初值。__init__。构造函数中不能有返回值。 四、析构函数 析构函数是构造函数的反函数,在销毁(释放)对象时调用它们。定义一个名为__del__的没有返回值和参数的函数。 五、实例成员变量 在之前的例子,MyClass类中成员变量message是类成员变量,即MyClass类和所有MyClass对象共享该成员变量。 问:如何定义属于每个对象自己的成员变量呢? 答:是在构造函数中定义self引用中的变量,这样的成员变量叫做实例成员变量。 class MyClass(object): message = "Hello World" def __init__(self,name = "Allen", color="black"): print(name, color) self.name = name self.color = color def show(self): print(self.message) print(self.name, self.color) def __del__(self): print("Destructor is called for %s" % self.name) inst = MyClass("ZHAO") inst.show() print(inst.color) 运行结果: ZHAO black Hello World ZHAO black black Destructor is called for ZHAO 六、静态函数和类函数 之前在本文中接触到的类成员函数均与实例绑定,只能通过对象访问而不能通过类名访问。 Python中支持两种基于类名访问成员函数: 静态函数 @staticmethod 类函数 @classmethod 他们不同点是,类函数有一个隐形参数cls可以用来获取类信息,而静态函数没有。 class MyClass(object): message = "Hello World" def __init__(self,name = "Allen", color="black"): print(name, color) self.name = name self.color = color def show(self): print(self.message) print(self.name, self.color) @staticmethod def printMessage(): print(MyClass.message) @classmethod def createdObj(cls, name, color): print(cls.__name__, name, color) return cls(name, color) def __del__(self): print("Destructor is called for %s" % self.name) MyClass.printMessage() inst = MyClass.createdObj("Mary", "red") print(inst.message) 运行结果: Hello World MyClass Mary red Mary red Hello World Destructor is called for Mary 在上述代码中,定义了静态函数printMessage(),在其中可以访问类成员变量MyClass.message,可以通过类名进行调用; 代码中还定义了类方法createdObj(),类方法定义中的第一个参数必须为隐形参数cls,在createdObj()中可以通过隐形参数cls替代类名本身,本例中的createdObj()建立并返回了一个MyClass实例. 七、私有成员 封装性是面向对象的重要特点,Python也提供了将不希望外部看到的成员隐藏起来的私有成员机制。但不想大多数编程语言用Public 、Private关键字表达可见范围的方法,Python使用指定变量名格式的方法定义私有成员。以双下滑想开头即为私有成员变量。

优秀的个人博客,低调大师

php中mysql操作的buffer知识

php与mysql的连接有三种方式,mysql,mysqli,pdo。不管使用哪种方式进行连接,都有使用buffer和不使用buffer的区别。 什么叫使用buffer和不使用buffer呢? 客户端与mysql服务端进行查询操作,查询操作的时候如果获取的数据量比较大,那个这个查询结果放在哪里呢? 有两个地方可以放:客户端的缓冲区和服务端的缓冲区。 我们这里说的buffer指的是客户端的缓冲区,如果查询结果已经从服务端获取回来了,放置在了客户端的缓冲区,我们就称之为使用buffer。如果还是存放在服务端的缓冲区的话,我们就说没有使用buffer(unbuffer)。 使用buffer和不使用buffer有什么区别? 主要在内存方面,使用buffer会增加客户端的内存压力,当返回的数据结果特别大的时候可能会占用调用客户端(实际就是一个php进程)比较大的进程。不使用buffer自然对服务端(这里说的是提供mysql服务的服务器)压力更大。 具体可以参考:PHP查询MySQL大量数据的内存占用分析 php中三种模式是如何设置是否使用buffer的? mysql默认的query是使用buffer的,而不使用buffer就需要使用mysql_unbuffer_query mysqli默认的query是不使用buffer的,要使用buffer就需要设置MYSQLI_STORE_RESULT pdo默认的quey是不使用buffer的,要使用buffer就需要设置MYSQL_ATTR_USE_BUFFERED_QUERY 大致相关代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 <?php $dbConfig = array ( 'host' => '10.128.11.101' , 'port' => '3306' , 'user' => 'test' , 'pass' => 'test' , 'db' => 'test' , ); $sql = 'select * from so_topic_app' ; //---------mysql----------// $db = mysql_connect( $dbConfig [ 'host' ], $dbConfig [ 'user' ], $dbConfig [ 'pass' ]); mysql_select_db( $dbConfig [ 'db' ], $db ); mysql_set_charset( 'utf8' , $db ); // mysql使用buffer $res = mysql_query( $sql , $db ); $data = array (); while ( $row = mysql_fetch_row( $res )) { $data [] = $row ; } // mysql不使用buffer $res = mysql_unbuffered_query( $sql , $db ); $data = array (); while ( $row = mysql_fetch_row( $res )) { $data [] = $row ; } mysql_close( $db ); //---------mysqli----------// $db = mysqli_connect( $dbConfig [ 'host' ], $dbConfig [ 'user' ], $dbConfig [ 'pass' ], $dbConfig [ 'db' ]); // mysqli不使用buffer $result = mysqli_query( $db , $sql ); $data = array (); while ( $row = $result ->fetch_array()) { $data [] = $row ; } // mysqli使用buffer $result = mysqli_query( $db , $sql , MYSQLI_STORE_RESULT); $data = array (); while ( $row = $result ->fetch_array()) { $data [] = $row ; } mysqli_free_result( $result ); mysqli_close( $db ); //---------pdo----------// $dsn = "mysql:dbname={$dbConfig['db']};host={$dbConfig['host']}" ; $pdo = new PDO( $dsn , $dbConfig [ 'user' ], $dbConfig [ 'pass' ]); // pdo不使用buffer $stmt = $pdo ->prepare( $sql ); $stmt ->execute(); $data = array (); $data = $stmt ->fetchAll(); // pdo使用buffer $pdo ->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); $stmt = $pdo ->prepare( $sql ); $stmt ->execute(); $data = array (); $data = $stmt ->fetchAll(); 后续 当然如果数据量非常大的话,大部分人还是会考虑使用分批次来提取和处理数据。所以实际上需要我们关注和使用mysql是使用buffer还是不使用buffer的场景非常少。 本文转自轩脉刃博客园博客,原文链接:http://www.cnblogs.com/yjf512/p/3431481.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

swift:入门知识之协议与扩展

swift中使用protocol声明一个协议接口 swift中类、枚举和结构体都可以实现协议接口 swift中类中的方法都可以修改成员变量的值 swift中结构体中的方法默认是不能修改成员变量的,添加mutating关键字后就可以修改了 swift中使用extension来为现有的类型添加功能。你可以使用扩展来给任意类型添加协议,甚至是你从外部库或者框架中导入的类型 swift中协议接口的委托代理模式和Objective-C基本上是一样的,都是主方设置一个代理人,然后将事情的处理委托给这个代理人去办,而主方自己只需要声明协议的规则即可。记住,代理人必须按照主方设定的规则去处理这些需要做的事情。不管代理人是谁,只要遵从协议就可以成为主方的委托者。 Swift 中的扩展可以: 1. 添加计算型属性和计算静态属性 2. 定义实例方法和类型方法 3. 提供新的构造器 4. 定义下标 5. 定义和使用新的嵌套类型 6. 使一个已有类型符合某个接口 具体举例如下: //声明一个ExampleProtocol协议,有一个成员变量和一个默认必须实现的方法 //声明一个协议 protocol ExampleProtocol{ //声明一个成员变量,并设置一个getter方法 var simpleDescription:String{get} mutating func adjust() } //定义一个SimpleClass类继承ExampleProtocol协议 //定义一个类继承协议 class SimpleClass: ExampleProtocol { var simpleDescription:String = "A very simple class" func adjust() { simpleDescription += " adjust" } } var simpleclass = SimpleClass() //创建对象 simpleclass.adjust() //实现下一中的必须实现的方法 simpleclass.simpleDescription //用get方法获取属性值 "A very simple class adjust" //定义一个SimpleStruct结构体继承ExampleProtocol协议 //定义一个结构体继承协议 struct SimpleStruct:ExampleProtocol { var simpleDescription:String = "A very simple struct" //必须用关键词mutating修饰,才能修改属性值 mutating func adjust() { simpleDescription += "struct" } } var simplestruct = SimpleStruct() //创建结构体成员变量 simplestruct.adjust() //实现下一中的必须实现的方法 simplestruct.simpleDescription //用get方法获取属性值 "A very simple structstruct --------------------------------------------------------------------------------------------------------- //声明一个Myprotocol协议,有一个成员变量和一个默认必须实现的方法,还有一个可选的方法 //定义一个有可选方法的协议 @objc protocol MyProtocol{ //声明一个成员变量,并设置一个getter和setter方法 var contentDescription:String {get set} func method() //可选的方法 optional func adjust() } //定义一个Boy类继承Myprotocol这个协议 class Boy:MyProtocol{ var contentDescription:String = "he is a boy" //实现可选的方法 func method() { contentDescription += ",good dood study" } //实现必须实现的方法 func adjust(){ contentDescription += ",okay" } } let boy = Boy() boy.contentDescription = "Tom" //set方法赋值 boy.method() //实现必须实现的方法 boy.adjust() //实现可选的方法 boy.contentDescription //get方法取值 "Tom ,good good study,okay" --------------------------------------------------------------------------------------------------------- //定义一个扩展继承ExampleProtocol协议 //定义一个类扩展 extension Int:ExampleProtocol{ var simpleDescription:String{ return "The number \(self)" } mutating func adjust(){ self += 24 } } 7.simpleDescription // 7 //自定义一个扩展 程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式! 分类: Swift开发技术 本文转自当天真遇到现实博客园博客,原文链接: http://www.cnblogs.com/XYQ-208910/p/4905128.html如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册