全面解读Python Web开发框架Django,利用Django构建web应用及其部署
全面解读Python Web开发框架Django
花了两周时间,利用工作间隙时间,开发了一个基于Django的项目任务管理Web应用。项目计划的实时动态,可以方便地被项目成员查看(^_^又 重复发明轮子了)。从前台到后台,好好折腾了一把,用到:HTML、CSS、JavaScript、Apache、Python、mod_wsgi、 Django。好久不用CSS和JavaScript了,感到有点生疏了,查了无数次手册。后台Django开发环境的搭建也花了不少时间和精力。记录下 来,免得以后走弯路。同时给大家推荐一下Django框架,如果你想非常快速地编写自己的web应用,可以考虑使用Django,同时Django还会给 你提供一个功能强大的后台管理界面。
Django是一个开源的Web应用框架,由Python写成。采用MVC的软件设计模式,主要目标是使得开发复杂的、数据库驱动的网站变得简单。 Django注重组件的重用性和“可插拔性”,敏捷开发和DRY法则(Don't Repeat Yoursef)。在Django中Python被普遍使用,甚至包括配置文件和数据模型。它可以运行在启用了mod_python或mod_wsgi的 Apache2,或者任何兼容WSGI(Web Server Gataway Interface)的Web服务器。
1. Django的快速开发
第一步(Model):设计自己的数据模型。
第二步(View):创建网页模板。Django自己的Html模板语言,非常容易将数据和模板结合起来,创建动态页面。
第三步(Control):定义URL,提供服务和控制。
入门教程: https://docs.djangoproject.com/en/1.4/intro/tutorial01/
2. Django开发环境的搭建
Django可以运行在任何遵守WSGI的Web服务器上。本文主要介绍Apache2+mod_wsgi+Django的环境搭建。所需要的软件如下:
Apache2:Web服务器
Python2.x:Python语言支持
mod_wsgi:Apache的WSGI模块,有了该模块的支持,就可以用Python做为CGI脚本来编写网络应用(之前还有一个
mod_python,在Apache官网上发现mod_python已经过时,渐渐要被mod_wsgi替代,据说mod_wsig性能要好一些)
Django:一个强大的Python Web开发框架,本文的主角。
2.1 Apache的安装
下 载: http://httpd.apache.org/download.cgi (选择版本2.2.22,mod_wsig暂不支持2.4.2)
解压缩 : $tar xvfz httpd-NN.tar.gz
$cd httpd-NN
编译配置: $./configure –with-included-apr –prefix=PREFIX #with-included-apr选项指定使用apache软件包里面的apr库
编 译: $make
安 装: $make install
配 置: $vim PREFIX/conf/httpd.conf
测 试: $PREFIX/bin/apachectl -k start
参 考:
官方主页: http://httpd.apache.org/
安装文档: http://httpd.apache.org/docs/2.2/install.html
2.2 Python的安装
下 载: http://www.python.org/getit/releases/2.7.3/ (选择2.X版都可以,3.0暂不支持)
解压缩 : $tar xvf python-X.tar
$cd python-Y
编译配置: $./configure –enable-shared –prefix=PREFIX #–enable-shared选项指定生成python的动态库
编 译: $make
安 装: $make install
测 试: $python
参 考:
官方主页: http://www.python.org/
2.3 mod_wsgi模块的安装
下 载: http://code.google.com/p/modwsgi/ (选择3.3版本)
解压缩 : $tar xvfz mod_wsgi.X.Y.tar.gz
$cd mod_wsgi.X.Y
编译配置: $././configure –with-apxs=/usr/local/apache2/bin/apxs –with-python=/usr/local/bin/python # 指定Apache2的模块编译程序和Python解析器
编 译: $make
安 装: $make install
测 试: $python
2.3.1 配置Apache(修改/usr/local/apche2/confi/httpd.conf)
1
2
3
4
5
6
7
8
9
|
# 加载wsgi模块 LoadModule wsgi_module modules/mod_wsgi.so .... # HTTP请求处理脚本 WSGIScriptAlias /test /home/xxx/www/test.wsgi < Directory "/home/xxx/www"> Order allow, deny Allow from all </ Directory > |
2.3.2 编写test.wsgi(WSGI标准: http://www.python.org/dev/peps/pep-3333/ )
1
2
3
4
5
6
7
8
9
|
def application(environ, start_response): status = '200 OK' output = 'Hello World!' response_headers = [( 'Content-type' , 'text/plain' ), ( 'Content-Length' , str ( len (output)))] start_response(status, response_headers) return [output] |
2.3.3 重启apche2
在任意网络浏览器中输入:http://www.mysite.com/test。看到“Hello World!”,恭喜你成功安装了WSGI模块。
参 考:
官方主页: http://code.google.com/p/modwsgi/
安装文档: http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide
配置文档: http://code.google.com/p/modwsgi/wiki/QuickConfigurationGuide
WSGI文档: http://www.python.org/dev/peps/pep-3333/
2.4 Django的安装
下 载: https://www.djangoproject.com/download/ (选择1.4版本)
解压缩 : $tar xvfz Django-1.4.tar.gz
$cd Django-1.4
安 装: $python setup.py install
测 试:
1
2
3
|
$python >>> import django >>> print (django.get_version()) |
参 考:
官方主页: https://www.djangoproject.com/
安装文档: https://docs.djangoproject.com/en/1.4/intro/install/
快速入门: https://docs.djangoproject.com/en/1.4/intro/tutorial01/
3. Django中文支持
Django使用的是UTF-8编码,所以对于国际化支持不成问题。因为初次玩Django,中文显示乱,折腾死人了(一直在用的的mysql默认
字符串是latin1编码,vim默认保存的文件编码为ascii)。最终得出结论,如果中文显示乱码,或者Django报错(… unicode
…blabla…),请检查:
Django的设置。打开自己项目的settings.py,LANGUAGE_CODE=”zh_CN” ?FILE_CHARSET='UTF-8′ ?DEFAULT_CHARSET='utf-8′?
查看自己项目所有的文件编码是否以UTF-8编码保存的?确保.py文件第一行要加上:#-*- coding:utf-8 -*- ?
HTML模板文件head部分,添加<meta http-equiv=“Content-Type” content=“text/html;charset=utf-8″/>
检查自己项目的数据库字符串编码是否为UTF-8,命令如下:
查看:
1
2
3
|
show create database dbname; show create table tablename; show full columns from tablename; |
创建:
1
2
|
create database dbname CHARACTER SET utf8; create table tblname CHARACTER SET utf8; |
修改:
1
2
|
alter database dbname CHARACTER SET = utf8; alter table tablename CONVERT TO CHARACTER SET utf8; |
4. Django应用的部署
Django应用的运行有两个方式,一种是在开发阶段,使用创建项目下面的manager.py runserver
ip:port来启动一个用Python实现的轻型web服务器;另外一种就是通过mod_wsgi将你自己的应用部署到生产环境,对外提供服务。下面简
单介绍一下Django的部署(虚拟主机上的配置,自行参考文档)。
假设你创建的Django项目文件列表如下:
1
2
3
4
5
6
7
8
9
10
11
|
my-site |- my-site |- myapp |-static |- ... |- static |- css |- js | ... |- apache |- ... |
4. 1. 创建Django项目的wsgi脚本(my-site/apache/django.wsgi),内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import os, sys sys.path.append( '/.../www/' ) sys.path.append( '/.../www/my-site' ) os.environ[ 'DJANGO_SETTINGS_MODULE' ] = 'my-site.settings' os.environ[ 'PYTHON_EGG_CACHE' ] = '/.../www/.python-eggs' import django.core.handlers.wsgi _application = django.core.handlers.wsgi.WSGIHandler() def application(environ, start_response): if environ[ 'wsgi.url_scheme' ] = = 'https' : environ[ 'HTTPS' ] = 'on' return _application(environ, start_response) |
4.2. 配置Apache(httpd.conf),内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 请求访问www.xxx.com/的时候,转到django.wsgi WSGIScriptAlias / /.../www/my-site/apache/django.wsgi <Directory /.../www/my-site/apache> Order deny,allow Allow from all </Directory> # 静态文件的访问路径配置 Alias /static/ /.../www/my-site/static/ <Directory /.../www/my-site/static> Order deny,allow Allow from all </Directory> |
4.3. 配置setting.py
EBUG=False
自定义404.html,500.html模板(网页未找到、服务器内部错误)
4.4. 静态文件
1
2
3
|
STATIC_ROOT = ‘/…/www/my-site/static/' STATIC_URL = ‘/static/' $./manager.py collectstatic |
注意:开发阶段,一般都会把相应app的静态文件,放在app目录下的static目录下。在正式生产环境部署的时候,使用./manager.py collectstatic来把所有静态文件收集到STATIC_ROOT指定的位置,包括管理后台的。
4.5. 重启apahce
浏览器输入相应的URL地址,看到你自己的web应用界面的话,恭喜大功告成!
5. 总结
本文主要介绍了一下Django开发环境的搭建、Django应用的部署和中文乱码的解决方法。具体如何使用Django快速地创建自己的web应
用,并没有提及。Django相对来说,文档比较齐全,加上官方推出的一本书:《The Django
Book》,相信只要开发环境搭建好,创建自己的Web应用也会非常容易。
进一步学习Django,请看:
Django1.4文档: https://docs.djangoproject.com/en/1.4/
Django Book 英文版: http://www.djangobook.com/en/2.0/
Django Book 中文版: http://djangobook.py3k.cn/2.0/
推荐两个非常好的教程:
The Django Book(中文版):我自己一开始是参考这个教程学习的,非常有意思的是这个教程中有大量的评论,几乎每段都有,从10年开始一直到现在。虽然这本书比较老,有很多内容都过时了,但在这些评论中有讲解,有勘误和最新的实践,这些评论让整个学习过程变得非常有趣。我自己也留下了不少评论,哈哈。
被解放的姜戈:这是博客园的另一位博友Vamei(中文读作,挖煤)写的关于Django简介的系列文章,基本概念,简单的实践都有。
创建一个名为mysite的Django项目的命令:
$ django-admin startproject mysite
$ cd mysite
$ python manage.py startapp myApp # 创建一个app,名称为myApp
一个刚创建的Django项目的目录结构
图1:django项目的基本结构
不是要点的要点
- 创建一个专门的用户,例如www-data:在后面部署Django的过程中,我遇到了自使用Linux以来,最多的权限问题。很大的原因是因为不同进程间需要通信,或相互访问,这就需要这些用户间都有某个文件的读写权限。例如两个进程都要访问同一个socket文件时,这两个进程的用户都要对该socket文件有读写权限。因此将所有这些进程都交给一个专门的用户来操作,可以避免很多权限问题。
- url是web开发的核心要素之一:在客户端,每一个url就是一个页面;从开发者来看,所有的开发都是围绕如何正确的路由这些url到正确的html文件来展开的。
- stackoverflow是一个很棒的网站,大部分的问题都可以在上面找到解决方案,如果没有找到答案,就提问吧,很快就可以得到回复。
概述
用户从浏览器访问一个django开发的页面,整个流程是这样的:
the web client <-> the web server <-> the socket <-> WSGI <-> Django
用户从浏览器访问一个url,该请求从用户发送到web server,web server通过socket(或者约定一个端口)与WSGI进行通信,再由WSGI将请求发给django。web server和WSGI都有很多种选择,常见的组合有Apache + mod_wsgi和nginx + uWSGI。我用的是nginx + uWSGI。下面先介绍几个名词。
web server:
虽然每个网页服务器程序有很多不同,但有一些共同的特点:每一个网页服务器程序都需要从网络接受HTTP请求,然后提供HTTP回复给请求者。HTTP回复一般包含一个HTML文件,有时也可以包含纯文本文件、图像或其他类型的文件。
一般来说这些文件都存储在网页服务器的本地文件系统里,而URL和本地文件名都有一个阶级组织结构的,服务器会简单的把URL对照到本地文件系统中。当正确安装和设置好网页服务器软件,服务器管理员会从服务器软件放置文件的地方指定一个本地路径名为根目录。
web server是面对用户请求的第一道门,有些请求由web server自己处理,例如静态文件的访问等;还有一些请求则交给WSGI处理,如对动态页面的访问。
socket: 最近在学习《计算机网络》这门课,socket相关的内容在网络模型中属于传输层,位于网络层和应用层之间。主要用于实现进程间通讯。在这里主要是实现nginx和uWSGI两个不同进程之间的通讯。
WSGI:
Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。自从WSGI被开发出来以后,许多其它语言中也出现了类似接口。
可以将WSGI看做是一种协议,据说之所以Python中有很多web框架,就是因为WSGI调用非常方便。uWSGI是WSGI这一协议的实现。在实际使用过程中,uWSGI代替了python manage.py runserver的作用,当然还有其他作用。
最早的Web服务器只支持静态html。随着网站也越来越复杂,出现了动态技术。但是服务器并不能直接运行 php,asp这样的文件,因此需要一个第三方,与第三方做个约定,我把请求参数发送给你,然后我接收你的处理结果给客户端。这个约定就是 common gateway interface,简称cgi。这个协议可以用vb,c,php,python 来实现。
简单地说,cgi是Web App与Http Server之间的桥梁。
除了cgi,还有wsgi(Web Service Gateway Interface)。WSGI所在层的位置低于CGI,与CGI不同的是WSGI具有很强的伸缩性且能运行于多线程或多进程的环境下,这是因为WSGI只是一份标准并没有定义如何去实现。实际上WSGI并非CGI,因为其位于web应用程序与web服务器之间,而web服务器可以是CGI。
所有均开始于settings文件
如果我们已经创建了一个hello应用(如何创建,请参考本文开始时提到的两个教程),并且使用下面的语句运行:
python manage.py runserver 0.0.0.0:8000
ROOT_URLCONF = 'mysite.urls'
# urls.py from django.conf.urls import url, include
from django.contrib import admin
from mysite.views import hello admin.autodiscover() urlpatterns = [
url(r'^hello/$', hello),
]
# viwes.py from django.http import HttpResponse def hello(request):
return HttpResponse("Hello World!")
uWSGI与Django之间的通讯
uWSGI与Django之间的通讯是通过wsgi.py文件实现的,这个文件在创建Django project后就生成了。这里的设置中最主要的一点就是正确的指出settings.py文件的位置。因为Django中的一切都始于这个文件。这些都使用Django的默认配置就可以了。
# wsgi.py
"""
WSGI config for mysite project. It exposes the WSGI callable as a module-level variable named ``application``. For more information on this file, see
https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/
""" import os from django.core.wsgi import get_wsgi_application # 这里已经指定了settings文件的位置
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") application = get_wsgi_application()
一些命令:
# 安装uwsgi
$ pip install uwsgi
# 如果 python manage.py runserver 0.0.0.0:8000 可以运行成功,则可以使用下面的命令代替django本身的runserver功能(为了可以使用8000端口,需要先修改防火墙设置)
$ uwsgi --http :8000 --module mysite.wsgi # 在项目的根目录运行该命令(与运行python manage.py的目录相同),参考"图1"中的目录结构
但是在与nginx联合使用时,端口号由nginx中的配置决定,在uwsgi中就不用配置端口号了(由于nginx与uwsgi之间通过socket通信,所以直接访问该端口号就可以访问整个django程序)。
下面是一个配置文件的例子,配置完成后可以在6001端口访问django程序,nginx与uwsgi之间通过"/run/uwsgi/metDNA.sock"进行通信。
server {
listen 6001;
server_name localhost; # 这里可以根据实际情况填写域名或localhost
charset utf-8;
client_max_body_size 50M; location / {
include uwsgi_params;
# include conf.d/corsheaders.conf;
uwsgi_pass unix:///run/uwsgi/metDNA.sock;
} # Django media
location /media { # 设置www.ourlab.cn/media的具体路径,这里是django项目配置文件中的`MEDIA_ROOT`的位置
# your Django project's media files - amend as required
alias /mnt/data/metdna-upload;
} location /static { # django项目的静态文件夹位置,即`STATIC_ROOT`的位置
# your Django project's static files - amend as required
# first project's static files path
alias /mnt/data/www/metDNA/metDNA/static/static_root;
}
}
更多详情,可以参考:https://uwsgi-docs.readthedocs.io/en/latest/tutorials/Django_and_nginx.html
uwsig的配置:
- 具体配置,可以参考这里;
- 因为在配置文件中设置了touch-reload参数,所以更新django代码后,可以使用 touch /path/to/wsgi.py 加载更新,而不用重启整个django项目;
- 使用uwsgi的emperor模式:/usr/bin/uwsgi --emperor /etc/uwsgi/sites,sites文件夹中放的是uwsgi的配置文件mysite.ini(可以放多个django项目相关的配置文件)。该模式始终处于运行状态,如果要停止该django项目,可以重命名sites文件夹下对应的配置文件;
Instances under the control of the emperor should never dies.
If you want to fully stop an instance, simply remove/rename/move the instance config file in a way that the emperor rule will never match.
nginx的配置
由于一开始不熟悉,因此nginx的配置花了很长时间。其中的关键点是:不同的server之间是无法共用端口号(port)的,因此如果要使用同一个端口号,例如80端口,那么所有的路由都要配置在一个server中。
- 配置文件位置:/etc/nginx/ ;
- 可以将自定义的配置文件放在目录 /etc/nginx/conf.d/,由于在主配置文件 /etc/nginx/nginx.conf 中包含有 include /etc/nginx/conf.d/*.conf; 这条语句,所以该目录下所有的 .conf 文件都会被加载;
- 自定义的配置文件,可以参考这里nginx.conf,主要是指定端口号和server name(网站域名);
值得注意的是,上面配置文件中的sock文件,例如unix:/run/uwsgi/lipidCCS.sock,nginx的用户和uwsgi的用户需要同时对其进行读写操作,因此都需要相应的权限。靠近前端用户的nginx和靠近后端django程序的uwsgi就是通过这个sock文件进行通信的。
一些命令:
# 重启服务:
$ sudo systemctl restart nginx.service
其他
进程管理:进程管理的工具也有很多,uWSGI中的Emperor mode与进程管理工具具有相似的功能,两者似乎无法共用。我选择的是Supervisor。
uwsgi程序中的Emperor mode,会与Supervisor相互影响;Emperor mode的作用与Supervisor差不多,但自己感觉Supervisor更好用一些
重大修订:
第一次修订于2017年12月20日,补充了uwsgi相关配置的说明,对配置文件添加了注释;
第二次修订于2018年12月27日,勘误以及修复链接(这是每年这个时间都要写网站的节奏啊!);
参考:
https://zh.wikipedia.org/wiki/%E7%B6%B2%E9%A0%81%E4%BC%BA%E6%9C%8D%E5%99%A8
https://zh.wikipedia.org/wiki/Berkeley%E5%A5%97%E6%8E%A5%E5%AD%97
https://zh.wikipedia.org/wiki/Web%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%BD%91%E5%85%B3%E6%8E%A5%E5%8F%A3
http://djangobook.py3k.cn/2.0/
http://uwsgi-docs.readthedocs.io/en/latest/tutorials/Django_and_nginx.html
https://www.quora.com/What-are-the-differences-between-nginx-and-gunicorn # 这里讲了为什么要使用nginx和gunicorn(作用同uWSGI)
http://data-eater.com/python-worker/
http://www.shellwjl.com/2016/03/07/ali_flask_wsgi_Nginx/
全面解读Python Web开发框架Django,利用Django构建web应用及其部署的更多相关文章
- 【ASP.NET Web API2】利用HttpClient调用Web API(TODO)
参照: 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用 纯属记录一下遇到的问题: 我们利用HttpClient来调用自宿主方式寄宿的Web API.HttpCl ...
- maven学习(下)利用Profile构建不同环境的部署包
接上回继续,项目开发好以后,通常要在多个环境部署,象我们公司多达5种环境:本机环境(local).(开发小组内自测的)开发环境(dev).(提供给测试团队的)测试环境(test).预发布环境(pre) ...
- 如何利用docker 构建golang线上部署环境
公司最近开发了一个项目是用golang 写的,现在要部署到线上环境去,又不想在服务器上装单独的golang,决定用docker 封装下,直接打到镜像里面,然后就直接在hub.docker.com上面搜 ...
- maven学习利用Profile构建不同环境的部署包
项目开发好以后,通常要在多个环境部署,象我们公司多达5种环境:本机环境(local).(开发小组内自测的)开发环境(dev).(提供给测试团队的)测试环境(test).预发布环境(pre).正式生产环 ...
- Maven 教程(18)— 利用 Profile 构建不同环境的部署包
原文地址:https://blog.csdn.net/liupeifeng3514/article/details/79776257 接上回继续,项目开发好以后,通常要在多个环境部署,象我们公司多达5 ...
- (八)利用 Profile 构建不同环境的部署包
接上回继续,项目开发好以后,通常要在多个环境部署,象我们公司多达5种环境:本机环境(local).(开发小组内自测的)开发环境(dev).(提供给测试团队的)测试环境(test).预发布环境(pre) ...
- [Python] 利用Django进行Web开发系列(一)
1 写在前面 在没有接触互联网这个行业的时候,我就一直很好奇网站是怎么构建的.现在虽然从事互联网相关的工作,但是也一直没有接触过Web开发之类的东西,但是兴趣终归还是要有的,而且是需要自己动手去实践的 ...
- 利用Django进行Web开发
Web就是用来表示Internet主机上供外界访问的资源的.网页也统称为web资源.Internet上供外界访问的Web资源主要分为如下两类: 静态web资源:指web页面中供人们浏览的数据始终是不变 ...
- 基于MVC4+EasyUI的Web开发框架经验总结(6)--在页面中应用下拉列表的处理
在很多Web界面中,我们都可以看到很多下拉列表的元素,有些是固定的,有些是动态的:有些是字典内容,有些是其他表里面的名称字段:有时候引用的是外键ID,有时候引用的是名称文本内容:正确快速使用下拉列表的 ...
- 基于MVC4+EasyUI的Web开发框架形成之旅--总体介绍
最近花了很多时间在重构和进一步提炼Winform开发框架的工作上,加上时不时有一些项目的开发工作,我博客里面介绍Web开发框架的文章比较少,其实以前在单位工作,80%的时间是做Web开发的,很早就形成 ...
随机推荐
- Vue Router 相关
1. 路由传参: 编程式的导航 router.push this.$router.push("home"); this.$router.push({ name: 'news', p ...
- 【水滴石穿】react-native忽略黄色提醒
方法一 import { YellowBox } from 'react-native'; YellowBox.ignoreWarnings(['Remote debugger']); // 忽略黄色 ...
- ubuntu上安装notepadpp
Notepad++是一套非常有特色的自由软件的纯文字编辑器(许可证:GPL).有完整的中文化接口及支持多国语言编写的功能(UTF8 技术).它的功能比 Windows 中的 Notepad(记事本)强 ...
- 关于JSON的个人理解
1.比xml更易于解析的数据存储方式 2.主要是用键值对的方式进行存储 3.可以用来存储对象或者是对象数组 个人感觉W3C上给的教程很好
- 阿里云CDN边缘脚本EdgeScript公测:简单语法完成CDN复杂配置
CDN可以将源站内容分发至最靠近用户侧的节点,使得用户就近获取内容,提高用户的访问成功率和效率.作为CDN运维工程师,他的日常工作就是通过CDN系统的配置和管理,来确保CDN业务正常运转,以此来保障网 ...
- JQuery-- 获取元素的宽高、获取浏览器的宽高和垂直滚动距离
* 能够使用jQuery设置尺寸 * .width() width * .innerWidth() width + padding * .outerWidth() width + padding + ...
- 读JS高性能总结——DOM编程(一)
DOM是一个与语言无关的API,它在浏览器中的借口却是用JS来实现的. 浏览器通常会把DOM和JS独立实现. 在IE中,JS的实现名是JScript,位于jscript.dll文件中,DOM实现则是m ...
- Adapter小练习
Aapter的继承关系图: Android中Adapter的是数据和视图之间的桥梁,数据在adapter中做处理,然后显示到视图上面. 一.ArrayAdapter适配器 java代码: import ...
- 【OI】拓扑排序
拓扑排序 首先要求图为DAG 算法:首先将度为1的节点加入队列每次取出队首点u,在图中删去和u相邻的边继续将度数为1的点加入队列 到了最后, 如果没有度数为1的点,则图不是DAG 通过拓扑排序可以给D ...
- #define 和常量 const 的区别
const 后的常量,程序对其中只能读不能修改. #include <iostream> using namespace std; int main() { const double pi ...