学习Python必须知道的关键点

2023-08-01 19:23:51 浏览数 (3)

Python涉及的细节知识点比较多,在学习的过程中,这些关键的知识点需要牢记,笔记如下。

基础概念

包的导入

一个模块被另一个程序第一次引入时,其主程序将运行。如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用__name__属性来使该程序块仅在该模块自身运行时执行。

代码语言:python代码运行次数:0复制
if__name__=='__main__':
    print('程序自身在运行')
else:
    print('我来自另一模块')

import语法会首先把item当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,一个exc:ImportError异常被抛出了。

反之,如果使用形如import item.subitem.subsubitem这种导入形式,除了最后一项,都必须是包,而最后一项则可以是模块或者是包,但是不可以是类,函数或者变量的名字。

如果包定义文件__init__.py存在一个叫做__all__的列表变量,那么在使用from package import*的时候就把这个列表中的所有名字作为包内容导入。作为包的作者,可别忘了在更新包之后保证__all__也更新了。如下代码:

代码语言:python代码运行次数:0复制
__all__=["echo","surround","reverse"]

字符串

如果你希望将输出的值转成字符串,可以使用repr()或str()函数来实现。

str()函数返回一个用户易读的表达形式。

repr()产生一个解释器易读的表达形式。

例如

代码语言:python代码运行次数:0复制
>>>s='Hello,world.'
>>>str(s)
'Hello,world.'
>>>repr(s)
"'Hello,world.'"

str.format()的基本使用

print('Wearethe{}whosay"{}!"'.format('knights','Ni'))

括号及其里面的字符(称作格式化字段)将会被format()中的参数替换,在括号中的数字用于指向传入对象在format()中的位置,如下所示:

print('{0}and{1}'.format('spam','eggs'))

如果在format()中使用了关键字参数,那么它们的值会指向使用该名字的参数。位置及关键字参数可以任意的结合。

print('Thestoryof{0},{1},and{other}.'.format('Bill','Manfred',other='Georg'))

'!a'(使用ascii()),'!s'(使用str())和'!r'(使用repr())可以用于在格式化某个值之前对其进行转化:

代码语言:txt复制
importmath
代码语言:txt复制
print('ThevalueofPIisapproximately{!r}.'.format(math.pi))

可选项':'和格式标识符可以跟着字段名。这就允许对值进行更好的格式化。下面的例子将Pi保留到小数点后三位:

代码语言:txt复制
print('ThevalueofPIisapproximately{0:.3f}.'.format(math.pi))

在':'后传入一个整数,可以保证该域至少有这么多的宽度。用于美化表格时很有用。

如果你有一个很长的格式化字符串,而你不想将它们分开,那么在格式化时通过变量名而非位置会是很好的事情。最简单的就是传入一个字典,然后使用方括号'[]'来访问键值:

代码语言:txt复制
table={'Sjoerd':4127,'Jack':4098,'Dcab':8637678}
代码语言:txt复制
print('Jack:{0[Jack]:d};Sjoerd:{0[Sjoerd]:d};Dcab:{0[Dcab]:d}'.format(table))

输出:

Jack:4098;Sjoerd:4127;Dcab:8637678

%操作符也可以实现字符串格式化。它将左边的参数作为类似sprintf()式的格式化字符串,而将右边的代入,然后返回格式化后的字符串.例如:

代码语言:txt复制
print('ThevalueofPIisapproximately%5.3f.'%math.pi)

因为str.format()比较新的函数,大多数的Python代码仍然使用%操作符。但是因为这种旧式的格式化最终会从该语言中移除,应该更多的使用str.format().

pickle序列化模块

pickle模块实现了基本的数据序列和反序列化。

  • 通过pickle模块的序列化操作我们能够将程序中运行的对象信息保存到文件中去,永久存储。
  • 通过pickle模块的反序列化操作,我们能够从文件中创建上一次程序保存的对象。

基本接口:

代码语言:txt复制
pickle.dump(obj,file,[,protocol])

有了pickle这个对象,就能对file以读取的形式打开:

代码语言:txt复制
x=pickle.load(file)

注解:从file中读取一个字符串,并将它重构为原来的python对象。file:类文件对象,有read()和readline()接口。

实例1:使用pickle模块将数据对象保存到文件

代码语言:txt复制
importpickle
代码语言:txt复制
data1={
代码语言:txt复制
'a':[1,2.0,3,4 6j],
代码语言:txt复制
'b':('string',u'Unicodestring'),
代码语言:txt复制
'c':None}
代码语言:txt复制
selfref_list=[1,2,3]
代码语言:txt复制
selfref_list.append(selfref_list)
代码语言:txt复制
output=open('data.pkl','wb')
代码语言:txt复制
#Pickled ictionary using protocol.
代码语言:txt复制
pickle.dump(data1,output)
代码语言:txt复制
#Picklethelistusingthehighestprotocolavailable.
代码语言:txt复制
pickle.dump(selfref_list,output,-1)
代码语言:txt复制
output.close()

实例2:使用pickle模块从文件中重构python对象

代码语言:txt复制
import pprint,pickle
代码语言:txt复制
pkl_file=open('data.pkl','rb')
代码语言:txt复制
data1=pickle.load(pkl_file)
代码语言:txt复制
pprint.pprint(data1)
代码语言:txt复制
data2=pickle.load(pkl_file)
代码语言:txt复制
pprint.pprint(data2)
代码语言:txt复制
pkl_file.close()

异常

一个except子句可以同时处理多个异常,这些异常将被放在一个括号里成为一个元组,例如:

代码语言:txt复制
except(RuntimeError,TypeError,NameError):
代码语言:txt复制
    pass

一个except子句可以忽略异常的名称,它将被当作通配符使用。你可以使用这种方法打印一个错误信息,然后再次把异常抛出。

代码语言:txt复制
try:
代码语言:txt复制
    f=open('myfile.txt')
代码语言:txt复制
    s=f.readline()
代码语言:txt复制
    i=int(s.strip())
代码语言:txt复制
except OSErroraserr:
代码语言:txt复制
    print("OSerror:{0}".format(err))
代码语言:txt复制
exceptValueError:
代码语言:txt复制
    print("Couldnotconvertdatatoaninteger.")
代码语言:txt复制
except:
代码语言:txt复制
    print("Unexpectederror:",sys.exc_info()[0])
代码语言:txt复制
    raise

try except语句还有一个可选的else子句,如果使用这个子句,那么必须放在所有的except子句之后。这个子句将在try子句没有发生任何异常的时候执行。例如:

代码语言:txt复制
for arg in sys.argv[1:]:
代码语言:txt复制
try:
代码语言:txt复制
    f=open(arg,'r')
代码语言:txt复制
except IOError:
代码语言:txt复制
    print('cannotopen',arg)
代码语言:txt复制
else:
代码语言:txt复制
    print(arg,'has',len(f.readlines()),'lines')
代码语言:txt复制
    f.close()

使用else子句比把所有的语句都放在try子句里面要好,这样可以避免一些意想不到的、而except又没有捕获的异常。

Python使用raise语句抛出一个指定的异常。raise唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是Exception的子类)。

如果你只想知道这是否抛出了一个异常,并不想去处理它,那么一个简单的raise语句就可以再次把它抛出。

代码语言:txt复制
try:
代码语言:txt复制
    raiseNameError('HiThere')
代码语言:txt复制
except NameError:
代码语言:txt复制
    print('Anexceptionflewby!')
代码语言:txt复制
    raise

输出异常信息如下:

代码语言:txt复制
Anexceptionflewby!
代码语言:txt复制
Traceback(mostrecentcalllast):
代码语言:txt复制
File"<stdin>",line2,in?
代码语言:txt复制
NameError:HiThere

如果一个异常在try子句里(或者在except和else子句里)被抛出,而又没有任何的except把它截住,那么这个异常会在finally子句执行后再次被抛出。

with关键字

关键词with语句就可以保证诸如文件之类的对象在使用完之后一定会正确的执行他的清理方法:

代码语言:txt复制
with open("myfile.txt") as f:
代码语言:txt复制
    for line in f:
代码语言:txt复制
        print(line, end="")

多继承

Python同样有限的支持多继承形式。多继承的类定义形如下例:

代码语言:txt复制
class DerivedClassName(Base1,Base2,Base3):
  • 类的私有属性 __private_attrs:两个下划线开头,声明该属性为私有,不能在类地外部被使用或直接访问。在类内部的方法中使用时self.__private_attrs。
  • 类的方法 在类地内部,使用def关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数self,且为第一个参数
  • 类的私有方法 __private_method:两个下划线开头,声明该方法为私有方法,不能在类地外部调用。在类的内部调用slef.__private_methods。
  • 类的专有方法:

<!---->

代码语言:txt复制
__init__:构造函数,在生成对象时调用
代码语言:txt复制
__del__:析构函数,释放对象时使用
代码语言:txt复制
__repr__:打印,转换
代码语言:txt复制
__setitem__:按照索引赋值
代码语言:txt复制
__getitem__:按照索引获取值
代码语言:txt复制
__len__:获得长度
代码语言:txt复制
__cmp__:比较运算
代码语言:txt复制
__call__:函数调用
代码语言:txt复制
__add__:加运算
代码语言:txt复制
__sub__:减运算
代码语言:txt复制
__mul__:乘运算
代码语言:txt复制
__div__:除运算
代码语言:txt复制
__mod__:求余运算
代码语言:txt复制
__pow__:乘方
  • 运算符重载 Python同样支持运算符重载,我么可以对类的专有方法进行重载,即重新实现即可。

正则表达式

Python的re模块提供了re.sub用于替换字符串中的匹配项。

代码语言:txt复制
re.sub(pattern,repl,string,max=0)

返回的字符串是在字符串中用RE最左边不重复的匹配来替换。如果模式没有发现,字符将被没有改变地返回。

可选参数count是模式匹配后替换的最大次数;count必须是非负整数。缺省值是0表示替换所有的匹配。

实例:

代码语言:txt复制
import re
代码语言:txt复制
phone="2004-959-559#这是一个电话号码"
代码语言:txt复制
#删除注释
代码语言:txt复制
num=re.sub(r'#.*$',"",phone)
代码语言:txt复制
print("电话号码:",num)
代码语言:txt复制
#移除非数字的内容
代码语言:txt复制
num=re.sub(r'D',"",phone)
代码语言:txt复制
print("电话号码:",num)

以上实例执行结果如下:

电话号码:2004-959-559

电话号码:2004959559

正则表达式修饰符-可选标志

正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位OR(|)它们来指定。如re.I|re.M被设置成I和M标志:

修饰符描述

re.I使匹配对大小写不敏感

re.L做本地化识别(locale-aware)匹配

re.M多行匹配,影响^和$

re.S使.匹配包括换行在内的所有字符

re.U根据Unicode字符集解析字符。这个标志影响w,W,b,B.

re.X该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。

re.match与re.search的区别

re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。

文件操作

f.seek()

如果要改变文件当前的位置,可以使用f.seek(offset,from_what)函数。

from_what的值,如果是0表示开头,如果是1表示当前位置,2表示文件的结尾,例如:

  • seek(x,0):从起始位置即文件首行首字符开始移动x个字符
  • seek(x,1):表示从当前位置往后移动x个字符
  • seek(-x,2):表示从文件的结尾往前移动x个字符 from_what值为默认为0,即文件开头。

Python依赖默认下载路径

Python会将下载的内容保存到/root/.cache目录下,是因为它使用了缓存机制来提高下载效率和减少网络流量。当你使用pip下载和安装Python包时,它会将包和依赖项保存到缓存目录中,以便下次使用时可以直接从缓存中获取,而不需要重新下载。

如果你想更改Python缓存目录的位置,可以设置环境变量XDG_CACHE_HOME来指定新的缓存目录。XDG_CACHE_HOME是一个标准的环境变量,用于指定用户的缓存目录。

以下是一个示例命令,用于将Python的缓存目录更改为/path/to/new/directory:

代码语言:shell复制
export XDG_CACHE_HOME=/path/to/new/directory

这个命令将会将XDG_CACHE_HOME环境变量设置为/path/to/new/directory。当你使用Python下载和安装包时,它将会将包和依赖项保存到这个目录下。

需要注意的是,修改缓存目录可能会影响到其他Python项目和用户。因此,在修改缓存目录时,你需要仔细考虑它的影响,并确保它不会影响到其他进程和用户。

Python 列表是引用传递

在Python中,如果将一个列表作为参数传递给函数,并在函数内部修改该列表,会影响函数外部的列表。这是因为在Python中,列表是可变对象,传递给函数的是列表对象的引用,而不是列表对象的副本。因此,对列表对象的任何修改都会影响到原始列表对象。

以下是一个示例代码,演示了在函数内部删除列表元素会影响函数外部的列表:

代码语言:python代码运行次数:0复制
def remove_element(my_list, element):
    my_list.remove(element)
    # del my_list[2]

my_list = [1, 2, 3, 4, 5]
print("Before:", my_list)

remove_element(my_list, 3)
print("After:", my_list)

如果不想在函数内部修改原始列表对象,可以在函数内部创建一个新的列表对象,并将原始列表对象的内容复制到新列表对象中。例如,可以使用以下代码来创建一个新的列表对象:

代码语言:python代码运行次数:0复制
new_list = my_list.copy()

然后,在函数内部修改新列表对象,而不是原始列表对象。这样就不会影响函数外部的列表对象了。

可变参数*args和**kwargs

可变参数*args 和 **kwargs定义函数时候,参数args在前,kwargs在后,*args和kwargs组合起来可以传入任意的参数。

*args参数:可接受任意个位置参数,当函数调用时,所有未使用(未匹配)的位置参数会在函数内自动组装进一个tuple对象中,此tuple对象会赋值给变量名args。

**kwargs参数:可接受任意个关键字参数,当函数调用时,所有未使用(未匹配)的关键字参数会在函数内组装进一个dict对象中,此dict对象会赋值给变量名kwargs。

同时使用*args和**kwargs时,*args参数列必须要在**kwargs前,要是像foo(1,a=1,b=2,c=3,2,3)这样调用的话,则会提示语法错误“SyntaxError: non-keyword arg after keyword arg”。

代码语言:python代码运行次数:0复制
def foo(*args,**kwargs):
    print 'args=',args,"len:",len(args)
    print 'kwargs=',kwargs,"len:",len(kwargs)
    print '**********************'
             
if __name__=='__main__':
    foo(1,2,3)
    foo(a=1,b=2,c=3)
    foo(1,2,3,a=1,b=2,c=3)
    foo(1,'b','c',a=1,b='b',c='c')

执行结果如下:

代码语言:txt复制
args= (1, 2, 3)
代码语言:txt复制
kwargs= {}
代码语言:txt复制
**********************
代码语言:txt复制
args= ()
代码语言:txt复制
kwargs= {'a': 1, 'c': 3, 'b': 2}
代码语言:txt复制
**********************
代码语言:txt复制
args= (1, 2, 3)
代码语言:txt复制
kwargs= {'a': 1, 'c': 3, 'b': 2}
代码语言:txt复制
**********************
代码语言:txt复制
args= (1, 'b', 'c')
代码语言:txt复制
kwargs= {'a': 1, 'c': 'c', 'b': 'b'}

For循环遍历

在序列中遍历时,索引位置和对应值可以使用enumerate()函数同时得到:

代码语言:python代码运行次数:0复制
fori, v in enumerate(['tic','tac','toe']):
    print(i, v)

输出:
0 tic
1 tac
2 toe

数据库操作

python使用pymysql包操作mysql数据库

代码语言:txt复制
#!/usr/bin/python3

import pymysql

#打开数据库连接
db=pymysql.connect("localhost","testuser","test123","TESTDB")

#使用cursor()方法获取操作游标
cursor=db.cursor()

#SQL插入语句
sql="INSERTINTOEMPLOYEE(FIRST_NAME,LAST_NAME,AGE,SEX,INCOME)
VALUES('%s','%s','%d','%c','%d')"%('Mac','Mohan',20,'M',2000)
try:
    #执行sql语句
    cursor.execute(sql)
    #执行sql语句
    db.commit()
except:
    #发生错误时回滚
    db.rollback()

#关闭数据库连接
db.close()

#获取所有记录列表
results=cursor.fetchall()
for row in results:
    print(row[0], row[1])

解析XML

python有三种方法解析XML,SAX,DOM,以及ElementTree:

1.SAX(simpleAPIforXML)

python标准库包含SAX解析器,SAX用事件驱动模型,通过在解析XML的过程中触发一个个的事件并调用用户定义的回调函数来处理XML文件。

SAX是一种基于事件驱动的API。利用SAX解析XML文档牵涉到两个部分:解析器和事件处理器。

  • 解析器负责读取XML文档,并向事件处理器发送事件,如元素开始跟元素结束事件;2.DOM(DocumentObjectModel)
  • 事件处理器则负责对事件作出相应,对传递的XML数据进行处理。

将XML数据在内存中解析成一个树,通过对树的操作来操作XML。

JSON解析

Python3中可以使用json模块来对JSON数据进行编解码,它包含了两个函数:

json.dumps():对数据进行编码。

json.loads():对数据进行解码。

在json的编解码过程中,python的原始类型与json类型会相互转换,具体的转化对照如下:

Python编码为JSON类型转换对应表:

代码语言:txt复制
Python	JSON
dict	object
list,tuple	array
str	string
int,float,int-&float-derivedEnums	number
True	true
False	false
None	null

JSON解码为Python类型转换对应表:

代码语言:txt复制
JSON	Python
object	dict
array	list
string	str
number(int)	int
number(real)	float
true	True
false	False
null	None

如果你要处理的是文件而不是字符串,你可以使用json.dump()和json.load()来编码和解码JSON数据。例如:

代码语言:txt复制
#写入JSON数据
wit hopen('data.json','w')asf:
    json.dump(data,f)

#读取数据
with open('data.json','r')asf:
    data=json.load(f)

时间

从返回浮点数的时间辍方式向时间元组转换,只要将浮点数传递给如localtime之类的函数。

代码语言:txt复制
#!/usr/bin/python3
import time

localtime=time.localtime(time.time())
print("本地时间为:",localtime)

以上实例输出结果:

本地时间为:time.struct_time(tm_year=2016,tm_mon=4,tm_mday=7,tm_hour=10,tm_min=28,tm_sec=49,tm_wday=3,tm_yday=98,tm_isdst=0)

assert断言

代码语言:txt复制
#如果条件不成立,则打印出'this is error'并抛出AssertionError异常
assert false,'this is error'

高级特性

socket编程

服务端代码

代码语言:txt复制
#!/usr/bin/python3
#文件名:server.py

#导入socket、sys模块
import socket
import sys

#创建socket对象
serversocket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

#获取本地主机名
host=socket.gethostname()

port=9999

#绑定端口
serversocket.bind(host,port)

#设置最大连接数,超过后排队
serversocket.listen(5)

whileTrue:
#建立客户端连接
clientsocket,addr=serversocket.accept()

print("连接地址:%s"%str(addr))

msg='HelloWorld!' "rn"
clientsocket.send(msg.encode('utf-8'))
clientsocket.close()

客户端代码

代码语言:txt复制
#!/usr/bin/python3
#文件名:client.py

#导入socket、sys模块
importsocket
importsys

#创建socket对象
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

#获取本地主机名
host=socket.gethostname()

#设置端口好
port=9999

#连接服务,指定主机和端口
s.connect(host,port)

#接收小于1024字节的数据
msg=s.recv(1024)

s.close()

print(msg.decode('utf-8'))

多线程:

代码语言:txt复制
#!/usr/bin/python3

import threading
import time

class myThread(threading.Thread):
    def__init__(self,threadID,name,counter):
        threading.Thread.__init__(self)
        self.threadID=threadID
        self.name=name
        self.counter=counter
        defrun(self):
        print("开启线程:" self.name)
        #获取锁,用于线程同步
        threadLock.acquire()
        print_time(self.name,self.counter,3)
        #释放锁,开启下一个线程
        threadLock.release()

    def print_time(threadName,delay,counter):
        while counter:
            time.sleep(delay)
            print("%s:%s"%(threadName,time.ctime(time.time())))
            counter-=1

threadLock=threading.Lock()
threads=[]

#创建新线程
thread1=myThread(1,"Thread-1",1)
thread2=myThread(2,"Thread-2",2)

#开启新线程
thread1.start()
thread2.start()

#添加线程到线程列表
threads.append(thread1)
threads.append(thread2)

#等待所有线程完成
for t in threads:
    t.join()
print("退出主线程")

队列

代码语言:txt复制
#!/usr/bin/python3

import queue
import threading
import time

exitFlag=0

class myThread(threading.Thread):
    def__init__(self,threadID,name,q):
        threading.Thread.__init__(self)
        self.threadID=threadID
        self.name=name
        self.q=q
    def run(self):
        print("开启线程:" self.name)
        process_data(self.name,self.q)
        print("退出线程:" self.name)

    def process_data(threadName,q):

while not exitFlag:
    queueLock.acquire()
    if not workQueue.empty():
        data=q.get()
        queueLock.release()
        print("%sprocessing%s"%(threadName,data))
    else:
        queueLock.release()
        time.sleep(1)

threadList=["Thread-1","Thread-2","Thread-3"]
nameList=["One","Two","Three","Four","Five"]
queueLock=threading.Lock()
workQueue=queue.Queue(10)
threads=[]
threadID=1

#创建新线程
fort Name in threadList:
    thread=myThread(threadID,tName,workQueue)
    thread.start()
    threads.append(thread)
    threadID =1

#填充队列
queueLock.acquire()
for word in nameList:
    workQueue.put(word)
queueLock.release()

#等待队列清空
while not workQueue.empty():
    pass

#通知线程是时候退出
exitFlag=1

#等待所有线程完成
for t in threads:
    t.join()
    print("退出主线程")

以上程序执行结果:

开启线程:Thread-1

开启线程:Thread-2

开启线程:Thread-3

Thread-3processingOne

Thread-1processingTwo

Thread-2processingThree

Thread-3processingFour

Thread-1processingFive

退出线程:Thread-3

退出线程:Thread-2

退出线程:Thread-1

退出主线程

debugger调试

可以通过pdb、IDE等工具进行调试。

Python中有两个内置方法在这里也很有帮助:

  • locals:执行locals()之后,返回一个字典,包含(currentscope)当前范围下的局部变量。
  • globals:执行globals()之后,返回一个字典,包含(currentscope)当前范围下的全局变量。

邮件发送

代码语言:txt复制
#!/usr/bin/python3

import smtplib
from email.mime.text import MIMEText
from email.header import Header

#第三方SMTP服务
mail_host="smtp.XXX.com"#设置服务器
mail_user="XXXX"#用户名
mail_pass="XXXXXX"#口令

sender='from@w3cschool.cn'
receivers=['429240967@qq.com']#接收邮件,可设置为你的QQ邮箱或者其他邮箱

message=MIMEText('Python邮件发送测试...','plain','utf-8')
message['From']=Header("hello world",'utf-8')
message['To']=Header("测试",'utf-8')

subject='PythonSMTP邮件测试'
message['Subject']=Header(subject,'utf-8')

try:
    smtpObj=smtplib.SMTP()
    smtpObj.connect(mail_host,25)#25为SMTP端口号
    smtpObj.login(mail_user,mail_pass)
    smtpObj.sendmail(sender,receivers,message.as_string())
    print("邮件发送成功")
except smtplib.SMTPException:
    print("Error:无法发送邮件")

CGI

通用网关接口(CommonGatewayInterface),它是一段程序,运行在服务器上如:HTTP服务器,提供同客户端HTML页面的接口。

所有的HTTP服务器执行CGI程序都保存在一个预先配置的目录。这个目录被称为CGI目录,并按照惯例,它被命名为/var/www/cgi-bin目录。

CGI文件的扩展名为.cgi,python也可以使用.py扩展名。

python cgi编程类似于jsp编程,获取get或post请求的处理方法如下:

代码语言:python代码运行次数:0复制
import cgi,cgitb

#创建FieldStorage的实例化
form=cgi.FieldStorage()

#获取数据
site_name=form.getvalue('name')
site_url=form.getvalue('url')

print("Content-type:text/html")
print()
print("<html>")
print("<head>")
print("<metacharset="utf-8">")
print("<title>CGI测试实例</title>")
print("</head>")
print("<body>")
print("<h2>%s官网:%s</h2>"%(site_name,site_url))
print("</body>")
print("</html>")

0 人点赞