VPS搭建网站

空间(虚拟主机,虚拟空间)

它是利用软件在服务器上硬盘上划分出来的一部分容量.共享的是服务器资源.
没有独立的IP和操作系统.开通以后只有一个FTP权限.它所支持的程序也是在服务器上默认配置好的.空间一般都装有面板,是傻瓜化的操作
由于多台虚拟主机共享一台真实主机的资源,每个用户所要承担的各类费用大幅度降低.

自从进入IT圈,以前使用国内的空间,感觉还行,速度快,但是后面的备案系统让大多数的站长跑到墙外来选择空间:
Godaddy、Dreamhost、Froghost、Photonvps等空间相信大多数的站长多多少少都会用过
但是随之而来的各种问题都让大家受到精神上的折磨,不过相信大家都是比较的喜欢捣鼓吧,从2012年开始很多的站长都选择到VPS行列中了,
以前高高在上的VPS现在已经沦为普通人都买得起用得起的产品了,但是想要好一点的,响应快一点的VPS主机价格还是很贵,

VPS还是比较适合对网站空间要求比较高的朋友来使用的,独立IP,完全自主的控制权限这些都是一般的虚拟主机所没有的.

自从2013年digitalocean把SSD的主机价格拉到5元每月之后,相信很多人都从Linode转过来了,不为什么,价格决定啊,而且性能上和高大上的Linode差别不是很多,
还是能得到大多数人的肯定,今年Vultr 的杀入,更是让大多数人目瞪口呆,因为价格更加便宜,而且内存上加到到768Ma,
而价格最优惠可以到2.5美金一个月,这样的产品绝对是对国人来说是一个杀伤力

VPS(Virtual Private Server)虚拟服务器

VPS就是将一台真正的服务器主机(可以理解为独立服务器),分为多台虚拟服务器主机.
但是虚拟出来的每一台服务器都有主机独立的内存、CPU、硬盘,因此在性能上相当一台真实存在的主机,但是成本却是很低,降低了初学者进入VPS主机门槛
VPS提供商:
Vultar,DigitalOcean,Linode,virmach, Bandwagon,国际阿里云

VPS除了可以用来做网站的服务器,还可以建立自己的ssh,vpn,shadowsocks等进行代理翻墙,可以一键快速配置Docker,GitLab,wonCloud.WordPress等其它应用.

Vultr VPS采用的是KVM虚拟化技术(openvz 还是 kvm 还是 xen 或者是 vmare ).

1.购买VPS:

vultr官网

全球主机交流

站长工具

2.购买域名

name.com 购买域名的优惠码: privacyplease 注意:目前只支持paypal或信用卡支持,不支持支付宝

3.设置DNS(将域名记录到此DNS服务器上),并将域名指定到VPS上(域名解析成IP)

DNS解析 可以通过My Account –> My Domains –>选择Quick Links这个下拉框的Manage Domain

推荐域名管理页面(添加解析域名记录)

将购买的域名放在dnspod下进行管理:便于管理多个域名,只要再修改下name.com的DNS,换成dnspod的域名解析服务器,
以后就可以在这里添加解析自己的域名记录了

修改DNS解析服务器

4.申请证书,支持泛域名

申请AlphaSSL免费证书教程

1.生成CSR,KEY文件
注意: 邮箱:自己常用的邮箱 域名:*.femnyy.com

1
2
3
4
# 也可以用命令行生成
openssl req -new -newkey rsa:2048 -nodes -keyout howsvps.key -out howsvps.csr
#注意在填写域名时,不需要添加二级域名:femnyy.com这样认证之后的这个证书,就可以是通配使用了
并没有将admin@femnyy.com的链接发到我的邮箱上,如果并没有给你发邮箱的话,

2.注册域名邮箱,本人使用的是qq注册域名邮箱

注意: 使用其他邮箱给 admin 邮箱发送一份邮件,能接收,说明没问题! 不能使用关联的QQ邮箱
如果不能接收,检查你的域名 MX / TXT 是否解析到其他地方了
别名就是主机记录,别名主机就是记录值
然后再加一个成员管理admin

3.免费SSL certificates
注意: 认证的邮箱 与上面的1步骤要一致

4.选择你刚刚注册的域名邮箱

5.邮件确认SSL证书申请,点击 I APPROVE,之后会再来一封.crt的文件 安装AlphaSLL教程

6.再将中级证书中,SHA-256 – Orders March 31, 2014 and After的代码,复制到.crt的文件,合成最终的.crt文件

7.再将.crt key文件 复制到服务器上,nginx的配置如下,同腾讯的一样配置

8.有了泛域名之后,就可以在 server_name 增加 *.femnyy.com;

4.上面的证书没有成功的话,那就用腾讯云SSL证书 免费一年

csr证书请求文件–交给CA签名后形成服务端自己的证书,认证通过之后,签证机构给你crt文件 crt+key = pem

cat crt key = pem

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
vim /etc/nginx/conf.d/ghost
server {
listen 80;
#server_name puplic IP;
#server_name 0.0.0.0; 上面的公网IP也不行了,
server_name www.femnyy.com puplic_IP yy.femnyy.com;
#server_name femnyy.com;
rewrite ^(.*) https://$host$1 permanent;
return 301 https://$server_name$request_uri;
}
server{
listen 443;
server_name www.femnyy.com;
ssl on;
#index index.htm index.html;
ssl_certificate /etc/nginx/conf.d/Nginx_ssl/server.crt;
ssl_certificate_key /etc/nginx/conf.d/Nginx_ssl/server.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:2368;
}
}
firewall-cmd --permanent --add-port=443/tcp
firewall-cmd --reload

推荐管理主机的状态 nodequery

统计访问量

其实在dnspod下管理域名时,就会有流量统计这个功能,但本人使用cloudflare加速网页之后,就没有此功能了
(可以看下本人的CND设置)

所以使用谷歌统计访问量
注册好了之后–>管理–> .js跟踪信息–>代码跟踪–>将网站跟踪的代码复制到ghost的code injection 的Blog Footer

如果有github学生礼包的话,就可以登陆
此链接,是教如何使用礼包教程
这样就可以免费使用digitalocean VPS和namecheap域名了(免费使用.me的域名一年).

使用digitalocean
1.gmail注册的,用paypal必须要充$5,其它支付方式,好像不需要
注册时比较坑的时,要等两个小时,害得我不断的去注册个人信息 我去(英语不好哈哈)!同时还比较麻烦的就是在关联paypal的时候,还会发信息给注册paypal的邮箱,并要求回复信息,认证是本人.
2.创建实例,居然是用Droplets这个单词

Share Comments

python 函数式编程

函数式编程最常见的技术就是对一个集合做Map,Reduce,Filter操作.这比起过程式的语言来说,在代码上要更容易阅读.(传统过程式的语言需要使用for/while循环,然后在各种变量中把数据倒过来倒过去的)

函数式编程的准则:不依赖于外部的数据,而且也不改变外部数据的值,而是返回一个新的值给你

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
def return_add_model_id(self, modelList):
func = lambda x: modify_idKeys(x) # 闭包
model_id = self._id
def modify_idKeys(goods):
g = goods.copy()
for keys, value in goods.items():
if keys == '_id':
g[model_id] = value
# g.pop('_id'),还保留着
return g
return list(map(func, modelList))
# 依次执行函数得到结果,并将结果组成一个新的list对象后进行返回
province_list = [{'name': '北京市', 'code': '11'}, \
{'name':'天津市','code':'12'},{'name':'河北省','code':'13'}]
remove_province_list = ['河北省']
# 函数式编程的filter 不仅可以做判断的依据,而且还可以修改其值
list(filter(lambda x: x.get('name') not in remove_province_list \
and x.pop('code'), province_list))
# [{'name': '北京市'}, {'name': '天津市'}]
a = list(map(lambda x, y : x*y, [1.2, 3, 5], [2, 4, 6]))
print(a)#[2.4,12,30]
a = reduce(lambda x,y:x+y,a)
print(a)#44.4
def toUpper(item):
return item.upper()
list(map(toUpper, ["hao", "chen", "coolshell"]))
# ['HAO', 'CHEN', 'COOLSHELL']
Share Comments

进程间通信(IPC)

程序(program)可执行文件(二进制文件)
程序的执行实例被称为进程(process),具有并行性,互不干扰的特点
Linux中的进程包含3段,和C差不多,分别为代码段,数据段和堆栈段

使用fork函数得到的子进程是继承了整个父进程的地址空间
exec()只是用另一个新程序替换了当前进程的正文,数据,堆和栈

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
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
pid_t result;
result = fork();
// fork之后是你进程先执行还是子进程先执行是不确定的
if(result == -1){
perror("fork");
exit;
}
else if(result == 0){// fork的返回值 如果在子进程中则返回0
printf("child process:%d\n My PID is %d\n",result,getpid());
if(execlp("ps","ps","-ef",NULL)<0){
perror("execlp error");
}
}
else{//如果在父进程中,则返回子进程ID
printf("father process:%d\n My PID is %d\n",result,getpid());
}
}

管道pipe:单向的,先进先出,无结构的,固定大小的字节流.数据读出后将从管道中移走,其它读进程都不能再读到这些数据.
进程试图读空管道时,在有数据定稿管道之前,里程将一直阻塞.同样,管道已经满时,进程再试图琯管道,在其它进程从管道中移走数据之前,写进程将一直阻塞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int pipe_fd[2];
if(pipe(pipe_fd)<0)
{
printf("pipe create error\n");
return -1;
}
else
{
printf("pipe create success \n");
}
// 其时的管理是创建在内核空间上的
close(pipe_fd[0]);//fd[0]文件描述符用于读取管道
close(pipe_fd[1]); //写入管道
}

父进程写,子进程读

有名管道(FIFO)

信号(signal)
共享内存
消息队列(FIFO)
信号量
套接字(Socket)

Share Comments

tornado

Tornado 是一个Python web框架和异步网络库,起初由 FriendFeed(后该公司被facebook收购,目前tornado由facebook开发维护)开发.由于非阻塞的特性,他在处理Http长连接、websocket等保持连接时间较长的请求时,并发能力很强.

Tornado是非阻塞式服务器,是实时Web服务的一个理想框架,大体上可以被分为4个主要的部分:

  1. web框架(RequestHandler,Application)
  2. HTTP的客户端和web服务端实现 (AsyncHTTPClient and HTTPServer)
  3. 异步网络库 (IOLoop and IOStream)
  4. 协程库 (tornado.gen) 允许异步代码写的更直接而不用链式回调的方式

web框架 (包括创建web应用的 RequestHandler 类,还有很多其他支持的类).

tornado.web.Application类的实例化:

传递给Application类init方法的最重要的参数是handlers(是一个元组组成的列表,其中每个元组的第一个元素是一个用于匹配的正则表达式,第二个元素是一个RequestHanlder类).它告诉Tornado应该用哪个类来响应请求(如果一个正则表达式包含一个捕获分组(即,正则表达式中的部分被括号括起来),匹配的内容将作为相应HTTP请求的参数传到RequestHandler对象中).settings参数则是对这个app的一些设置.

一旦Application对象被创建,我们可以将其传递给Tornado的HTTPServer对象,然后使用我们在命令行指定的端口进行监听(通过options对象取出.)最后,在程序准备好接收HTTP请求后,我们创建一个Tornado的IOLoop的实例.

Application对象是负责全局配置的,包括映射请求转发给处理程序的路由表.static,template的内容.Application实例中的所有属性,BaseHandler中使用Application实例中的属性,以便所有的Handler都可以使用其属性.

Tornado的请求处理函数类.当处理一个请求时,Tornado将这个类实例化,并调用与HTTP请求方法所对应的方法(get,post方法)

tornado.web.RequestHandler的生命周期是initialize() -> prepare() -> get()/post() -> on_finish()

  1. initialize() 在构造函数后调用,一般用于定义参数,不可异步

  2. prepare() 在具体的get()/post()/put()/delete()等执行前调用,一般用于加载登录信息,过滤请求等,可异步

  3. get()/post() 处理请求

  4. on_finish() 请求后的清理,保存缓存,session等,该方法不能传递任何数据到客户端,所以不能操作cookie,可异步

  5. getcurrentuser() 第一次使用实例中的currentuser参数且为None时会调用该方法,因为不能异步,所以需要通过异步来获取的,请写在prepare()中,blogxtg便是在prepare()中异步从redis读取.

RequestHandler类有一系列有用的内建方法,包括get_argument

RequestHandler的另一个有用的方法是write,它以一个字符串作为函数的参数,并将其写入到HTTP响应中

Tornado默认实现了几个常用的处理器

1
2
3
4
ErrorHandler :生成指定状态码的错误响应.
RedirectHandler :重定向请求.
StaticFileHandler :处理静态文件请求.
FallbackHandler :使可以在Tornado中混合使用其他HTTP服务器.

其它的Web 模块
  1. escape - XHTML, JSON, URL 的编码/解码方法

  2. database - 对 MySQLdb 的简单封装,使其更容易使用

  3. template - 基于 Python 的 web 模板系统

  4. httpclient - 非阻塞式 HTTP 客户端,它被设计用来和 web 及 httpserver 协同工作

  5. auth - 第三方认证的实现(包括 Google OpenID/OAuth、Facebook Platform、Yahoo BBAuth、FriendFeed OpenID/OAuth、Twitter OAuth)

  6. locale - 针对本地化和翻译的支持

  7. options - 命令行和配置文件解析工具,针对服务器环境做了优化

  8. Tornado可以解析URLencoded和multipart结构的POST请求

Tornado模板是被Python表达式和控制语句标记的简单文本文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Tornado模板是被Python表达式和控制语句标记的简单文本文件.
# 填充表达式:{{}},控制流语句{% %}{% end %}
# 你也可以在你的控制语句块中间使用{% set foo = 'bar' %}来设置变量.
# template_path参数告诉Tornado在哪里寻找模板文件
# 模板是一个允许你嵌入Python代码片段的HTML文件.
# 在模板中使用参数
{{wood}}
{% for book in books %}
<li>{{ book }}</li>
{% end %} }}
# 在模板中使用函数
# 渲染模板
self.render('index.html')
# 这段代码告诉Tornado在templates文件夹下找到一个名为index.html的文件,
# 读取其中的内容,并且发送给浏览器.
# 继承模板与重写标签
{% extend base.html%}
{% block body %}重写body block
# 子模板不能在block标签定义内容.

如果你想编写一个可扩展的社交应用、实时分析引擎,或RESTful API,那么简单而强大的Python,以及Tornado正是为你准备的!

提供静态文件
1
2
# static_path参数指定了你应用程序放置静态资源(如图像、CSS文件、JavaScript文件等)的目录
<link rel="stylesheet" href="{{ static_url("style.css") }}">
模板扩展(模块)

大多数站点希望复用像header、footer和布局网格这样的内容.在这一章中,我们将看到如何使用扩展Tornado模板或UI模块完成这一工作.一个拥有样式、布局和header/footer细节的主模版,以及一个处理页面的轻量级的子模板.

tornado通过extends和block语句支持模板继承,这就让你拥有了编写能够在合适的地方复用的流体模板的控制权和灵活性.

1
2
3
4
5
6
7
# 为了扩展一个已经存在的模板,你只需要在新的模板文件的顶部放上一句
# {% extends "filename.html" %}.
# 比如,为了在新模板中扩展一个父模板(在这里假设为main.html),你可以这样使用:
{% extends "main.html" %}
# 这就使得新文件继承main.html的所有标签,并且覆写为期望的内容.
UI模块是封装模板中包含的标记、样式以及行为的可复用组件(使模板部分模块化)

1.它所定义的元素通常用于多个模板交叉复用或在同一个模板中重复使用

2.模块本身是一个继承自Tornado的UIModule类的简单Python类,并定义了一个render方法.
当一个模板使用 module Foo(…) 标签引用一个模块时,Tornado的模板引擎调用模块的render方法,然后返回一个字符串来替换模板中的模块标签.

UI模块也可以在渲染后的页面中嵌入自己的JavaScript和CSS文件,或指定额外包含的JavaScript或CSS文件.
你可以定义可选的embedded_javascript、embedded_css、javascript_files和css_files方法来实现这一方法

1
{% module xsrf_form_html() %}跨网站攻击

3.一个非常有用的做法是让模块指向一个模板文件而不是在模块类中直接渲染字符串

Tornado默认会自动转义(escape)模板中的内容,把标签转换为相应的HTML实体,而不会被作为一个HTML元素解释(以免被浏览器执行).防止你的访客进行恶意攻击的.

举个例子,如果Burt想在footer中使用模板变量设置email联系链接,他将不会得到期望的HTML链接.考虑下面的模板片段:

1
2
3
4
5
6
{% set mailLink = "<a href="mailto:contact@burtsbooks.com">Contact Us</a>" %}
{{ mailLink }}'
# 它会在页面源代码中渲染成如下代码:
&lt;a href=&quot;mailto:contact@burtsbooks.com&quot;&gt;Contact Us&lt;/a&gt;
# 此时自动转义被运行了,很明显,这无法让人们联系上Burt.

为了处理这种情况,你可以禁用自动转义,一种方法是在Application构造函数中传递autoescape=None,另一种方法是在每页的基础上修改自动转义行为,如下所示:

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
{% autoescape None %}
{{ mailLink }}
# 这些autoescape块不需要结束标签,并且可以设置xhtml_escape来开启自动转义(默认行为)
# 或None来关闭.
# 然而,在理想的情况下,你希望保持自动转义开启以便继续防护你的网站.
# 因此,你可以使用{% raw %}指令来输出不转义的内容.
{% raw mailLink %}
# 需要特别注意的是,当你使用诸如Tornado的linkify()和xsrf_form_html()函数时
# 自动转义的设置被改变了.所以如果你希望在前面代码的footer中使用linkify()来包含链接
# 你可以使用一个{% raw %}块:
{% block footer %}
<p>
For more information about our selection, hours or events, please email us at
<a href="mailto:contact@burtsbooks.com">contact@burtsbooks.com</a>.
</p>
<p class="small">
Follow us on Facebook at
{% raw linkify("https://fb.me/burtsbooks", extra_params='ref=website') %}.
{% apply linkify or 自定义方法%} Application中get_template_namespace
</p>
{% end %}
# 这样,你可以既利用linkify()简记的好处,又可以保持在其他地方自动转义的好处 }}' }}

异步网络库 (IOLoop and IOStream), 为HTTP组件提供构建模块,也可以用来实现其他协议.

一般的web应用是阻塞性质的,也就是说当一个请求被处理时,这个进程就会被挂起直至请求完成.这意味着应用程序被有效的锁定直至处理结束,很明显这在可扩展性上出现了问题.

不过,Tornado给了我们更好的方法来处理这种情况.应用程序在等待第一个处理完成的过程中,让I/O循环打开以便服务于其他客户端,直到处理完成时启动一个请求并给予反馈,而不再是等待请求完成的过程中挂起进程.

同时实时web功能需要为每个用户提供一个多数时间被闲置的长连接, 在传统的同步web服务器中,这意味着要为每个用户提供一个线程, 当然每个线程的开销都是很昂贵的.

为了尽量减少并发连接造成的开销,Tornado使用了一种单线程事件循环的方式. 这就意味着所有的应用代码都应该是异步非阻塞的, 因为在同一时间只有一个操作是有效的
IOLoop模块

一般的异步回调
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
import time
import threading
def long_io(callback):
"""将耗时的操作交给另一线程来处理"""
def fun(cb): # 回调函数作为参数
"""耗时操作"""
print( "开始执行IO操作")
time.sleep(5)
print("完成IO操作,并执行回调函数")
cb("io result") # 执行回调函数
threading._start_new_thread(fun, (callback,)) # 开启线程执行耗时操作
def on_finish(ret):
"""回调函数"""
print( "开始执行回调函数on_finish")
print( "ret: %s" % ret)
print( "完成执行回调函数on_finish")
# 可读性差,要结合多个函数
def req_a():
"""将req_q逻辑拆分了"""
print( "开始处理请求req_a" )
long_io(on_finish)
print( "离开处理请求req_a")
def req_b():
print( "开始处理请求req_b")
time.sleep(2) # 添加此句来突出显示程序执行的过程
print( "完成处理请求req_b")
def main():
req_a()
req_b()
while 1: # 添加此句防止程序退出,保证线程可以执行完
pass
if __name__ == '__main__':
main()
# 开始处理请求req_a
# 离开处理请求req_a
# 开始处理请求req_b
# 开始执行IO操作
# 完成处理请求req_b
# 完成IO操作,并执行回调函数
# 开始执行回调函数on_finish
# ret: io result
# 完成执行回调函数on_finish
yield控制进程的暂停与send又恢复
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
import time
import threading
gen = None # 全局生成器,供long_io使用
def long_io():
def fun():
print("开始执行IO操作")
global gen
time.sleep(5)
try:
print("完成IO操作,并send结果唤醒挂起程序继续执行")
gen.send("io result") # 使用send返回结果并唤醒程序继续执行
except StopIteration: # 捕获生成器完成迭代,防止程序退出
pass
threading._start_new_thread(fun, ())
def req_a():
"""将req_q逻辑组织在一起"""
print("开始处理请求req_a")
ret = yield long_io()
print("ret: %s" % ret)
print("完成处理请求req_a")
def req_b():
print("开始处理请求req_b")
time.sleep(2)
print( "完成处理请求req_b")
def main():
global gen
gen = req_a() # 得到生成器,不能像同步一样去输写
next(gen) # 开启生成器req_a的执行
req_b()
while 1:
pass
if __name__ == '__main__':
main()
# 开始处理请求req_a
# 开始处理请求req_b
# 开始执行IO操作
# 完成处理请求req_b
# 完成IO操作,并send结果唤醒挂起程序继续执行
# ret: io result
# 完成处理请求req_a
改进yield
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
import time
import threading
gen = None # 全局生成器,供long_io使用
def gen_coroutine(f):
def wrapper(*args, **kwargs):
global gen
gen = f()
next(gen)
return wrapper
def long_io():
def fun():
print("开始执行IO操作")
global gen
time.sleep(5)
try:
print("完成IO操作,并send结果唤醒挂起程序继续执行")
gen.send("io result") # 使用send返回结果并唤醒程序继续执行
except StopIteration: # 捕获生成器完成迭代,防止程序退出
pass
threading._start_new_thread(fun, ())
@gen_coroutine
def req_a():
print("开始处理请求req_a")
ret = yield long_io()
print("ret: %s" % ret)
print("完成处理请求req_a")
def req_b():
print("开始处理请求req_b")
time.sleep(2)
print( "完成处理请求req_b")
def main():
req_a()
req_b()
while 1:
pass
if __name__ == '__main__':
main()
理解Tornado异步编程原理
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
import time
import threading
def gen_coroutine(f):
def wrapper(*args, **kwargs):
gen_f = f() # gen_f为生成器对象(req_a的代码,以及上下文)
r = next(gen_f) # 相当于调用long_io()
# r是long_io的生成器对象(long_io代码,以及上下文)
def fun(g):
ret = next(g) # 执行生成器long_io
try:
gen_f.send(ret) # 将结果返回给req_a并使其继续执行
except StopIteration:
pass
threading._start_new_thread(fun, (r,))
# tornado中是交给IOLoop进行管理
return wrapper
def long_io():
print("开始执行IO操作")
time.sleep(5)
print("完成IO操作,yield回操作结果")
yield "io result"
@gen_coroutine
def req_a():
print("开始处理请求req_a")
ret = yield long_io()
print("ret: %s" % ret)
print("完成处理请求req_a")
def req_b():
print("开始处理请求req_b")
time.sleep(2)
print( "完成处理请求req_b")
def main():
req_a()
req_b()
while 1:
pass
if __name__ == '__main__':
main()

但tornado是单线程的,实现异步的机制不是线程,而是epoll,即将异步过程交给epoll执行并进行监视回调

Tornado默认在函数处理返回时关闭客户端的连接.在通常情况下,这正是你想要的.但是当我们处理一个需要回调函数的异步请求时,我们需要连接保持开启状态直到回调函数执行完毕.

tornado.web.asynchronous 装饰器可以用来代替异步处理. 当使用这个装饰器的时候, 响应不会自动发送; 而请求将一直保持开放直到callback调用 RequestHandler.finish

1
2
3
4
5
6
7
8
9
10
11
12
13
class MainHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
def get(self):
http = tornado.httpclient.AsyncHTTPClient()
http.fetch("http://friendfeed-api.com/v2/feed/bret",
callback=self.on_response)
def on_response(self, response):
if response.error: raise tornado.web.HTTPError(500)
json = tornado.escape.json_decode(response.body)
self.write("Fetched " + str(len(json["entries"])) + " entries "
"from the FriendFeed API")
self.finish()

记住当你使用@tornado.web.asynchonous装饰器时,Tornado永远不会自己关闭连接.
你必须在你的RequestHandler对象中调用finish方法来显式地告诉Tornado关闭连接.(否则,请求将可能挂起,浏览器可能不会显示我们已经发送给客户端的数据.)

目前非阻塞IO的框架在近几年的web技术中特别火,比如node.js,而tornado依靠底层基于epoll(Linux)或者kqueue(BSD和MAC OSX)的IOLoop实现非阻塞IO,而且经过FriendFeed的实践,已经证明他是绝对优秀可靠的非阻塞IO框架,再加上他协程特性,让基于他的异步代码可以像阻塞多线程框架的同步代码一样易读易维护.

协程库 (tornado.gen) 允许异步代码写的像同步代码一样直观,而不用链式回调的方式.

tornado.gen模块,可以提供一个更整洁的方式来执行异步请求.

tornado.gen.Task对象的一个实例,将我们想要的调用和传给该调用函数的参数传递给那个函数.
这里,yield的使用返回程序对Tornado的控制,允许在HTTP请求进行中执行其他任务.
当HTTP请求完成时,RequestHandler方法在其停止的地方恢复.这种构建的美在于它在请求处理程序中返回HTTP响应,而不是回调函数中

Tornado的异步功能可以非常方便的创建依赖于缓慢查询或外部服务的Web应用
使用 @tornado.gen.coroutine 装饰器是做异步(协程)最简单的方式. 这允许你使用 yield 关键 字执行非阻塞I/O, 并且直到协程返回才发送响应.

1
2
3
4
5
6
7
8
class MainHandler(tornado.web.RequestHandler):
@tornado.gen.coroutine
def get(self):
http = tornado.httpclient.AsyncHTTPClient()
response = yield http.fetch("http://friendfeed-api.com/v2/feed/bret")
json = tornado.escape.json_decode(response.body)
self.write("Fetched " + str(len(json["entries"])) + " entries "
"from the FriendFeed API")

协程使用了Python的 yield 关键字代替链式回调来将程序挂起和恢复执行

调用协程时,几乎所有的情况下, 任何一个调用协程的函数都必须是协程它自身, 并且在 调用的时候使用 yield 关键字

HTTP的服务端和客户端实现 (HTTPServer and AsyncHTTPClient).

单线程server
tornado不仅仅是一个web framework,他还是一个简易的web server,这让他可以直接作为一个server来接收处理http请求,而不需要依靠wsgi容器.但是这个webserver过于简单,只支持单进程,所以在生产环境中,官方推荐的多进程多主机部署,启动多个tornado server实例分别监听不同端口,在上层通过类似nginx的成熟高效的http server来做负载均衡,将请求转发到合适端口的tornado实例中.(参考tornado官方文档运行部署篇)

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import json
import sys
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.gen
import tornado.httpclient
from tornado.options import define, options
import tornado.web
import os.path
define("port", default=8080, type=int)
class MainHandler(tornado.web.RequestHandler):
#并行协程
@tornado.gen.coroutine
def get(self):
ips = ["14.130.112.24",
"15.130.112.24",
"16.130.112.24",
"17.130.112.24"]
# 直到get_ip_info有返回了,才重新唤醒
rep1, rep2 = yield [self.get_ip_info(ips[0]), self.get_ip_info(ips[1])]
rep34_dict = yield dict(rep3=self.get_ip_info(ips[2]), rep4=self.get_ip_info(ips[3]))
self.write_response(ips[0], rep1)
self.write_response(ips[1], rep2)
self.write_response(ips[2], rep34_dict['rep3'])
self.write_response(ips[3], rep34_dict['rep4'])
def write_response(self, ip, response):
self.write(ip)
self.write(":<br/>")
if 1 == response["ret"]:
self.write(u"国家:%s 省份: %s 城市: %s<br/>" % (response["country"], response["province"], response["city"]))
else:
self.write("查询IP信息错误<br/>")
@tornado.gen.coroutine
def get_ip_info(self, ip):
http = tornado.httpclient.AsyncHTTPClient()
response = yield http.fetch("http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=" + ip)
if response.error:
rep = {"ret:0"}
else:
rep = json.loads(response.body)
# raise tornado.gen.Return(rep) # 此处需要注意python2
return rep
# Web应用App
class Application(tornado.web.Application):
def __init__(self):
settings = dict(
template_path=os.path.join(os.path.dirname(__file__), "templates"),
static_path=os.path.join(os.path.dirname(__file__), "media"),
xsrf_cookies=False,
cookie_secret="bZJc2sWbQLKos6GkHn/VB9oXwQt8S0R0kRvJ5/xJ89E=",
login_url="/a/user/login",
debug=True,
)
# self.db = torndb.Connection(
# host = 'localhost',
# database = 'test',
# user = 'root',
# password = 'root',
# )
super(Application, self).__init__([(r'/',MainHandler)], **settings)
def main():
tornado.options.parse_command_line()
# tornado.options.parse_config_file("https8010.conf")
ssl_options = {
"certfile": os.path.join(os.path.abspath("."), "213976088220462.pem"),#crt
"keyfile": os.path.join(os.path.abspath("."), "213976088220462.key"), # server.key
}
# http_server = tornado.httpserver.HTTPServer(Application(),ssl_options=ssl_options,xheaders=True)#ssl_options=ssl_options
# 创建http(socket)服务器实例
print(sys.getrecursionlimit())
print(options.port)
# http_server.listen(options.port)
Application().listen(options.port)
io = tornado.ioloop.IOLoop.current()# 异步网络库IOLoop
# 返回当前线程的IOLoop实例
io.start()
# 启动IOLoop实例的I/O循环,同时服务器监听被打开
# app = Application()
# app.listen(options.port) 对http_server的简写方式
# 其listen(端口)方法用来创建一个http服务器实例,并绑定到给定端口(注意:此时服务器并未开启监听)
if __name__ == "__main__":
main()
使用Tornado进行长轮询(应该有点 即时通讯IM)

Tornado异步架构的另一个优势是它能够轻松处理HTTP长轮询.这是一个处理实时更新的方法,它既可以应用到简单的数字标记通知,也可以实现复杂的多用户聊天室.

在大多数情况下,你至少希望将结果缓存以便两次相同搜索项的请求不会导致再次向远程API执行完整请求.

在前端使用像JavaScript这样的工具处理异步应用,让客户端承担更多工作,以提高你应用的扩展性.

部署提供实时更新的Web应用对于Web程序员而言是一项长期的挑战.更新用户状态、发送新消息提醒、或者任何一个需要在初始文档完成加载后由服务器向浏览器发送消息方法的全局活动.

所谓的”服务器推送”技术允许Web应用实时发布更新,同时保持合理的资源使用以及确保可预知的扩展.对于一个可行的服务器推送技术而言,它必须在现有的浏览器上表现良好.最流行的技术是让浏览器发起连接来模拟服务器推送更新.这种方式的HTTP连接被称为长轮询或Comet请求.

长轮询意味着浏览器只需启动一个HTTP请求,其连接的服务器会有意保持开启.浏览器只需要等待更新可用时服务器”推送”响应.当服务器发送响应并关闭连接后,(或者浏览器端客户请求超时),客户端只需打开一个新的连接并等待下一个更新.

长轮询的好处

HTTP长轮询的主要吸引力在于其极大地减少了Web服务器的负载.相对于客户端制造大量的短而频繁的请求(以及每次处理HTTP头部产生的开销),服务器端只有当其接收一个初始请求和再次发送响应时处理连接.大部分时间没有新的数据,连接也不会消耗任何处理器资源.

浏览器兼容性是另一个巨大的好处.任何支持AJAX请求的浏览器都可以执行推送请求.不需要任何浏览器插件或其他附加组件.对比其他服务器端推送技术,HTTP长轮询最终成为了被广泛使用的少数几个可行方案之一.

我们已经接触过长轮询的一些使用.实际上,前面提到的状态更新、消息通知以及聊天消息都是目前流行的网站功能.像Google Docs这样的站点使用长轮询同步协作,两个人可以同时编辑文档并看到对方的改变.Twitter使用长轮询指示浏览器在新状态更新可用时展示通知.Facebook使用这项技术在其聊天功能中.长轮询如此流行的一个原因是它改善了应用的用户体验:访客不再需要不断地刷新页面来获取最新的内容

Share Comments

闭包

在python中,不管是定义变量还是方法,还是类,实际上你定义的时候,分配了内存空间,仅仅是使用一个变量名指向了这一块的内存空间而已.
变量名引用值和函数
重名方法导致代码无效

闭包(Closure)又称函数闭包(function closures):内部函数对外部函数作⽤域⾥变量的引⽤(⾮全局变量),则称内部函数为闭包

引用了自由变量的函数.这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外.所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体.闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例

在一些语言中,在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包.运行时,一旦外部的 函数被执行,一个闭包就形成了,闭包中包含了内部函数的代码,以及所需外部函数中的变量的引用.

闭包被广泛使用于函数式编程语言

闭包可以形象的把它理解为一个封闭的包裹(封闭作用域),这个包裹就是一个函数,当然还有函数内部对应的逻辑,包裹里面的东西就是自由变量,自由变量可以在随着包裹到处游荡.当然还得有个前提,这个包裹是被创建出来的.

1
2
3
4
5
6
7
8
def func(name):
def inner_func(age): # 闭包
# name为自由变量
print( 'name:', name, 'age:', age)
return inner_func
bb = func('the5fire')
bb(26)
# name: the5fire age: 26

这里面调用func的时候就产生了一个闭包——inner_func,并且该闭包持有自由变量——name,因此这也意味着,当函数func的生命周期结束之后,name这个变量依然存在,因为它被闭包引用了,所以不会被回收

nonlocal访问外部函数的局部变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def counter(start=0):
def incr():
nonlocal start
start += 1
return start
return incr
c1 = counter(5)
print(c1()) # 6
print(c1()) # 7
c2 = counter(50)
print(c2()) # 51
print(c2()) # 52
print(c1()) # 8
print(c1()) # 9

闭包思考:

1.闭包似优化了变量,原来需要类对象完成的⼯作,闭包也可以完成 2.由于闭包引⽤了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存

闭包减少参数传递,同时增加代码的可移植性
Share Comments

python内建属性

Python在启动的时候会⾃动为我们载⼊很多内建的函数,类,属性,⽐如dict,list,type,print,这些都位于builtin 模块中,可以使⽤dir(builtin)来查看.
这也是为什么我们在没有import任何模块的情况下,就能使⽤这么多丰富的函数和功能了.

在Python中,有⼀个内建模块,该模块中有⼀些常⽤函数;在Python启动后,且没有执⾏程序员所写的任何代码前,Python会⾸先加载该内建函数到内存.

另外,该内建模块中的功能可以直接使⽤,不⽤在其前添加内建模块前缀

常见的内建属性

对象的内建属性

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
class Itcast(object):
def __init__(self,subject1):
self.subject1 = subject1
self.subject2 = 'cpp'
#属性访问时拦截器,只要访问属性就必须先调用此方法(包括方法(方法也是对象))
# 所以此方法中,不能包含访问属性的语句:比如self.subject1
def __getattribute__(self,obj):
print("====1>%s"%obj)
if obj == 'subject1':
print('log subject1')
return 'redirect python'
else:
temp = object.__getattribute__(self,obj)
print("====2>%s"%str(temp))
return temp
def show(self):
print('this is Itcast')
s = Itcast("python")
print(s.subject1)
print(s.subject2)
#1. 先获取show属性对应的结果,,,,应该是一个方法
#2. 再调用方法show()
s.show()
# import types
# p1.eat = types.MethodType(eat, p1)
# ====1>subject1
# log subject1
# redirect python
# ====1>subject2
# ====2>cpp
# cpp
# ====1>show
# ====2><bound method Itcast.show of <__main__.Itcast object at 0x0>>
# this is Itcast

Share Comments

python的动态性

动态语⾔:可以在运⾏的过程中,修改代码
静态语⾔:运行前必须先编,译编译时已经确定好代码,运⾏过程中不能修改

python是动态语⾔:它是⼀类在运⾏时可以改变其结构的语⾔:例如新的函数、对象、 甚⾄代码可以被引进,已有的函数可以被删除或是其他结构上的变化.

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
class Person(object):
def __init__(self, newName, newAge):
self.name = newName
self.age = newAge
# __slots__ = ("name", "age")
# 为了达到限制的⽬的,Python允许在定义class的时候
# 定义⼀个特殊的 __slots__变量,来限制该class实例能添加的属性
@staticmethod
def test();
pass
@classmethod
def testclass(cls):
pass
laowang = Person("老王", 10000)
print(laowang.name)
print(laowang.age)
# 可以添加其它属性和方法,即使在类只没有定义
laowang.addr = "北京...."
print(laowang.addr)
# 给对象和类添加属性
laozhao = Person("老赵", 18)
#print(laozhao.addr)
Person.num = 100
print(laowang.num)
print(laozhao.num)
# 添加静态方法
Persion.test = test
Persion.testclass = testclass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 添加方法
class Person(object):
def __init__(self, newName, newAge):
self.name = newName
self.age = newAge
def eat(self):
print("-----%s正在吃----"%self.name)
def run(self):
print("-----%s正在跑----"%self.name)
p1 = Person("p1", 10)
p1.eat()
p1.run = run
# p1.run()#虽然p1对象中 run属性已经指向了函数,但是这句代码还不正确
#因为run属性指向的函数 是后来添加的,p1.run()的时候,并没有把p1当做第
#1个参数,导致了第10行的函数调用的时候, 出现缺少参数的问题
import types
p1.run = types.MethodType(run, p1)
p1.run()
Share Comments

深浅拷贝

is 是⽐较两个引⽤是否指向了同⼀个对象(引⽤⽐较)

== 是⽐较两个对象内容是否相等

1
2
3
4
5
6
7
8
9
10
a = [1,2,3]
b = [1,2,3]
a == b # True
a is b # Flase
c = copy.deepcopy(a)# 深拷贝
a == c # True
a is c # Flase
id(a) # 指向a的内存地址
d = a # id(d)和id(a)值相等 浅拷贝
a is d # True

拷贝:一个变量引用另外一个变量

深拷⻉是对于⼀个对象所有层次的拷⻉(递归)
深拷贝

浅拷⻉是对于⼀个对象的顶层拷⻉,只是拷⻉了引⽤,并没有拷⻉内容(没重新生成新的内存地址)
浅拷贝列表
浅拷⻉对不可变类型和可变类型的copy不同
浅拷贝元组

Share Comments

多任务实现之进程-process

单核CPU多任务实现的原理,是由操作系统中的调度算法:

  1. 时间片轮转
  2. 优先级调度
    并发:看着像一起执行

多核才有并行

fork()创建新进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import os
ret = os.fork()# 创建一个新进程,父子进程都从这里开始
# 父进程的返回值会大于0(子进程ID),而子进程的返回值为0
print(ret)
# 父进程返回的是子进程ID,子进程中返回的是0
if ret>0:
print("---父进程--%d-"%os.getpid())
else:
print("---子进程---%d-%d-"%(os.getpid(),os.getppid()))
print("----over---")
# 13854
# ---父进程--13748-
# ----over---
# 0
# ---子进程---13846-13748-
# ----over---

多次fork的进程数 = 2的^fork次数的增长,所以要防止fork炸弹

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
import os
import time
#父进程
ret = os.fork()
if ret==0:
#子进程
print("--1--")
else:
#父进程
print("--2--")
#父子进程
ret = os.fork()
if ret==0:
#孙子
#儿子
print("--11--")
else:
#儿子
#父进程
print("--22--")
# --2--
# --22--
# --11--
# --1--
# --11--
# --22--

跨平台创建进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from multiprocessing import Process
import time
# 用Process创建的子进程,会保证所有的子进程运行完成
def test():
for _ in range(5):
print("---test---")
time.sleep(1)
if __name__ == '__main__':
p = Process(target=test)
p.start() #让这个进程开始执行test函数里的代码
# p.join()# 堵塞住进程,保证先完成子进程的代码
p.join(1) # 表示主进程愿意等待子进程完成的时间,到了这个时间之后,
# 不管子进程有没有完成自己的代码,父进程都会去执行了
for _ in range(3):
print("---main---")
time.sleep(1)
# ---test---
# ---main---
# ---main------test---
# ---test---
#
# ---main------test---
#
# ---test---

进程池

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
from multiprocessing import Pool
import os
import time
def worker(num):
for i in range(2):
print("===pid=%d==num=%d="%(os.getpid(), num))
time.sleep(1)
if __name__ == '__main__':
# 进程池中对最多保存3个进程
pool = Pool(3)
for i in range(5):
print("---%d---"%i)
# 向进程池中添加任务
# 注意:如果添加的任务数量超过了 进程池中进程的个数的话,那么不会导致添加不进入
# 添加到进程中的任务 如果还没有被执行的话,那么此时 他们会等待进程池中的
# 进程完成一个任务之后,会自动的去用刚刚的那个进程 完成当前的新任务
pool.apply_async(worker, (i,))# 非阻塞子进程,使用进程池的所有进程
# pool.apply 主进程使用进程池中的一个进程,
# 要等到这个进程工作完成之后才运行另一个进程,也就失去了多进程的意义
pool.close()#关闭进程池,相当于不能够再次添加新任务了
pool.join()# 主进程 创建/添加任务后,主进程默认不会等待进程池中的任务执行完后才结束
# 而是当主进程的任务做完之后立马结束,如果这个地方没join,会导致
# 进程池中的任务不会执行
# ---0---
# ---1---
# ---2---
# ---3---
# ---4---
# ===pid=15225==num=1=
# ===pid=15224==num=2=
# ===pid=15223==num=0=
# ===pid=15225==num=1====pid=15224==num=2====pid=15223==num=0=
#
#
# ===pid=15224==num=3====pid=15223==num=4=
#
# ===pid=15223==num=4====pid=15224==num=3=

进程间通信Queue(队列:先进先出)

想要完成进程间的数据共享,需要一些方法:命名管道(pipe)/信号(sign)/无名管道/共享内存/消息队列(缓存)/网络(socket)等

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
#修改import中的Queue为Manager
import os
from multiprocessing import Manager,Pool
def writer(q):
print("writer启动(%s),⽗进程为(%s)"%(os.getpid(),os.getppid()))
for i in "dongGe":
q.put(i)
def reader(q):
print("reader启动(%s),⽗进程为(%s)"%(os.getpid(),os.getppid()))
for i in range(q.qsize()):
print("reader从Queue获取到消息:%s"%q.get(True))
if __name__=="__main__":
print("(%s) start"%os.getpid())
q=Manager().Queue() # 使⽤Manager中的Queue来初始化
po=Pool()
# 使⽤阻塞模式创建进程
# 这样就不需要在reader中使⽤死循环了,可以让writer完全执⾏完成后,再⽤reader去读取
po.apply(writer, (q,))
po.apply(reader, (q,))
po.close()
po.join()
print("(%s) End" % os.getpid())
# (13748)start
# (13748)End
# writer启动(15731),⽗进程为(13748)
# reader启动(15733),⽗进程为(13748)
# reader从Queue获取到消息:d
# reader从Queue获取到消息:o
# reader从Queue获取到消息:n
# reader从Queue获取到消息:g
# reader从Queue获取到消息:G
# reader从Queue获取到消息:e
Share Comments

python元类

元类就是类的类,是用来创建这些类(对象)的

1
2
3
4
5
6
7
#使⽤元类创建出⼀个对象,这个对象称为"类"
MyClass = MetaClass()
#使⽤"类"来创建出实例对象
MyObject=MyClass()
Animal = type("Animal",(),{})
a = Animal()

类就是一组用来描述如何生成一个对象的代码段(数据结构和操作这些数据结构的方法组成)
但python中 类也是对象

类本身以及由类创建出来的实例统统都称之为对象,python中一切皆对象,unix一切皆文件

Python中所有的东⻄,指所有的东⻄——都是对象.这包括整数、字符串、函数以及类

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
class Person():
# 如下的打印说明:只要你使⽤关键字class
# Python解释器在执⾏的时候就会创建⼀个对象
print('iiii')
pass
p = Person
# type()查看对象是由哪个类创建出来的
# 打印元类:说明类对象是由元类创建的
print(type(p))
# 打印对象
print(100) # 类型在python在其实就是类,100就是由Int类创建的对象
print("femn".__class__)
print(p) # 类也是对象,才能打印出来
print(type(p()))
print(type(100))
# 实例对象是calss创建的,class类也是对象,那这个对象是由type()创建的,
# type(类名, 由⽗类名称组成的元组(针对继承的情况,可以为空),包含属 性的字典(名称和值))
Person2 = type("Person2",(),{})
print(type(Person2()))
# iiii
# <class 'type'>
# 100
# <class 'str'>
# <class '__main__.Person'>
# <class '__main__.Person'>
# <class 'int'>
# <class '__main__.Person2'>

使用type()元类动态创建类,并为此类添加方法

不推荐用元类创建类,还是用class创建类

Share Comments