目录

一.python面向对象

1.类和对象的定义

2.单继承

3.多继承

4.成员属性

5.异常

6.@property

二.python文件操作

文件的打开和读写

文件对象接口汇总

三.python中的多线程和多进程

1.多线程

2.线程锁

3.多进程

四.多进程间通信

1.管道

2.消息队列

3.共享内存

五.python网络编程

1.tcp服务器编程步骤

2.tcp客户端

3.udp服务器和客户端

4.socketsever

5.httpserver

六.python串口编程


一.python面向对象

在C++中我们学习了面向对象的编程思想,引入了类和对象的概念,我们将具有相同属性和行为的对象进行抽象形成了类,而对象是依据类产生的一个实例。

1.类和对象的定义

类:

1.类是同一类对象的属性和行为的抽象和总结

2.类描述同一类对象应包含的数据

3.类描述同一类对象的行为特征

4.类是抽象的,是一个概念模型

5.一个类可以找到多个对象

6.类是对象的模板,对象是该类的实例

类的定义:

class 类名:

类的属性

类的方法(用 def声明)

class Hero:# 魔法方法,类似于构造函数,用来初始化对象,在定义类对象的时候系统自动调用该函数def __init__(self, name, skill, perm):# 成员函数中必须通过self点号访问其他成员# 设置该类的成员属性,并且赋初值self.name = nameself.skill = skillself.perm = permdef __del__(self):print("销毁释放")def info(self):print("英雄【{}】使用了【{}】技能,造成了【{}%】的伤害".format(self.name, self.skill, self.perm))# __name__魔法方法,表示指定该文件中的代码入口位置
# 用于当前文件的测试代码段,在其他.py文件中,是不会访问到该代码段
if __name__ == "__main__":# 定义初始化类对象luban = Hero("鲁班", "大炮", 30)luban.info()

魔法方法:

1.在python中以两个下划线开头的方法叫做魔法方法,比如__init__,__str__等。

2.魔法方法在类或对象的某些事件触发后会自动执行,当然你也可以根据自己的需求来进行重写。

3.在定义类方法时,除了魔法方法,建议不要以双下划线__为前缀

下面我把列出一些常见的魔法方法:

self

类对象访问成员,形参self等价于this指针,是不需要给实参的,指向当前调用的类对象,某个对象调用其方法时,python解释器会把这个对象作为第一个参数传递给self,所以开发者只需要传递后面的参数即可

2.单继承

和C++中的继承概念一样,python中也有继承,子类会获取父类已有的所有属性和成员函数。这里我们就不过多介绍它的语法细节了,在C++的继承那章节我详细介绍过。我们直接来看一段代码进行介绍:

# 基类
class Luban:def __init__(self, name):self.name = nameself.skill = "嘴炮"self.level = 20def attack(self):print("英雄{}使用了最强技能{},有{}%概率暴击".format(self.name, self.skill, self.level))# 定义一个类,继承于Luban类,单继承class luban(Luban):# pass关键字,通过,不做任何事情,只是代码行的占位passdef __init__(self, name, skill, perm):self.name = nameself.skill = skillself.perm = perm# 定义基类同名函数,基类该函数被隐藏def attack(self):print("英雄{}使用了普通技能{},有{}无敌".format(self.name, self.skill, self.perm))# 设计一个成员函数,调用基类的成员函数,必须将self子类对象地址传递进去def oldAttack(self):# 基类名调用基类的构造函数用来初始化基类部分Luban.__init__(self, self.name)Luban.attack(self)if __name__ == "__main__":lu7 = luban("鲁班七号", "星空炮", 50)lu7.attack()lu7.oldAttack()

可以看到父类和子类由同名的函数,这时候子类的同名函数会将继承的父类的同名函数隐藏,所以我们在调用该函数的时候,会调用子类的该函数,如果我们要调用父类的同名函数,我们就需要在子类中再设计一个成员函数,并且使用基类名调用基类的构造函数来初始化基类部分,且需要将self子对象地址传参过去。

来看看运行结果:

3.多继承

既然有单继承,那么肯定也有多重继承,在多继承中,如果出现同名函数情况,在基类初始化顺序中,先初始化的将被后初始化的基类成员覆盖,与继承顺序无关。

class Luban:def __init__(self, name):self.name = nameself.skill = "嘴炮"self.level = 20def attack(self):print("英雄{}使用了最强技能{},有{}%概率暴击".format(self.name, self.skill, self.level))class Space:def __init__(self, name):self.name = nameself.skill = "火箭嘴炮"self.level = 40def attack(self):print("英雄{}使用了普通技能{},有{}%概率无敌".format(self.name, self.skill, self.level))class luban(Space, Luban):def __init__(self):Space.__init__(self, "鲁班")Luban.__init__(self, "鲁班大师")if __name__ == "__main__":lu7 = luban()lu7.attack()

4.成员属性

和C++一样,python的类中也有私有成员这样的属性来使一些成员数据更加安全,私有成员总是在类体中,以__开头,表示不能被对象.号访问,只能通过在类中设计一个成员函数来进行访问。

class luban:def __init__(self):# 私有成员总是在类体中,以__开头,表示不能被对象.号访问self.__name = "123"def setValue(self, name):self.__name = namedef getValue(self):return self.__nameif __name__ == "__main__":lu7 = luban()# 此处不报错,因为在给lu7对象新增一个__name公有成员属性# lu7.__name = "1234"# 此处会报错# print("name: ", lu7.__name)print(lu7.getValue())lu7.setValue("鲁班")print(lu7.getValue())

5.异常

当python检测到一个错误时,解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的异常,比如当我们打开了一个不存在的文件。程序停止执行并且提示错误信息这个动作,我们通常称之为:抛出异常,通过异常捕获可以针对突发事件做集中的处理,而保证程序的稳定性和健壮性。

语法格式:

try:

可能会出现异常的语句

except (异常类型1...........)

也可以获得异常的具体内容将其打印出来

except 异常类型  as  error

print(error)

def func(x, y):if y == 0:# 主动抛出异常raise ZeroDivisionError("分母为零")return x/yif __name__ == "__main__":x = int(input("x: "))y = int(input("y: "))try:  # 可能抛出异常的代码段ret = func(x, y)print("ret: ", ret)# except (ZeroDivisionError,):   # 接收异常,使用元组类型,元组中必须使用逗号表示#    print("..................")except (ZeroDivisionError, ) as fd:   # as表示将异常信息保存在后面的变量中print("*******************", fd)

6.@property

在设置类成员变量时,虽然写来很简单,但是没办法检查参数,且对外开放,任何人都可随意修改变量不安全,但是我们调用方法对私有成员进行访问又太复杂,那么我们就可以使用property。下面我们来看看代码

@property:说明下面的函数可以像成员变量一样.号访问,不需要()调用

@ .setter:说明下面的函数可以像成员变量一样,=号赋值,而不需要调用传参

class luban:def __init__(self):# 私有成员总是在类体中,以__开头,表示不能被对象.号访问self.__name = "123"# 说明下面的函数可以像成员变量一样.号访问,不需要()调用@propertydef Value(self):return self.__name# 说明下面的函数可以像成员变量一样,=号赋值,而不需要调用传参@Value.setterdef Value(self, name):self.__name = name@propertydef getValue(self):return self.__name# 错误,在此代码之前的类体中没有对setValue函数做过定义,因此@ *.setter只能是前面定义过的函数名# @setValue.setter错误@getValue.setter  # 正确def setValue(self, name):self.__name = nameif __name__ == "__main__":lu7 = luban()# 相当于调用Value()函数,返回成员变量的内容print(lu7.Value)lu7.Value = "鲁班"  # 相当于调用Value("鲁班") 函数,设置私有成员的内容print(lu7.Value)print(lu7.getValue)lu7.setValue = "鲁班大师"print(lu7.getValue)

二.python文件操作

文件的打开和读写

open()方法用于打开一个文件,并返回文件对象,在对文件进行处理过程都需要使用到这个函数,若文件无法打开,会抛出OSError

常规打开文件,使用 with open ... as fd, 打开文件,保存open返回值到fd中

with open("file.txt", "r+", encoding="utf-8") as fd:print("open")str1 = input("请输入:")# 将 str1中的数据写入到文件中fd.write(str1)list1 = ["\none\n", "teo\n", "three\n"]# 一次操作一行数据,参数要求是个列表类型,列表中元素必须是str类型fd.writelines(list1)# 指定从文件中读取相应字节的数据,返回读取到的数据str2 = fd.read(23)print(str2)# seek函数以某个基准点将文件指针进行偏移fd.seek(0, SEEK_SET)# readline(n)默认读取一行完整数据,n表示读取一行的前n个字节str1 = fd.readline(2)print(str1)# readlines(n)默认读取文件所有行的数据# n为0或非零: 0表示读取所有行,非零:表示只读取一行,关闭读取所有行数据的能力list1 = fd.readlines()print(list1)# 关闭打开的文件fd.close()

文件对象接口汇总

三.python中的多线程和多进程

1.多线程

我们先回顾一下什么是线程?线程也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程的全部资源,一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。

多线程的优势?

线程在程序中是独立的,并发的执行流。与分隔的进程相比,进程中线程之间的隔离程度要小,他们共享内存,文件,句柄和其他进程应有的状态。并且多线程的程序并发现高。进程在执行过程之中拥有独立的内存单元,而多个线程共享内存,极大提升了程序的运行效率,并且线程之间容易实现通信。

那么在python中我们如何创建线程呢?

通过threading库中的thread类就可以创建线程

from time import ctime, sleep
import threadingdef music(name):for i in range(5):print("I was listening to music <{}>,{}".format(name, ctime()))sleep(1)
def study(name):for i in range(5):print("I was study from {}, {}".format(name, ctime()))sleep(1)if __name__ == "__main__":# 通过threading库中的thread类,创建线程,指定线程的执行函数,通过args元组传递参数t1 = threading.Thread(target=music, args=("<nice day>", ))t2 = threading.Thread(target=study, args=("<game>", ))# 启动线程t1.start()t2.start()# 阻塞等待回收资源t1.join()t2.join()print("now time: {}".format(ctime()))

2.线程锁

说到多线程那么自然要提到线程间的同步和互斥,为了保护临界资源,我们常常要在多线程编程中使用到线程锁和信号量等来实现同步和互斥。

通过threading库中的lock类即可创建线程锁来达到互斥保护临界资源的效果

from time import ctime, sleep
import threadingdef music(name, lock):while True:with lock:    # with lock:添加自动锁,使用前加锁用完后解锁print("I was listening to music <{}>,{}".format(name, ctime()))sleep(1)
def study(name, lock):while True:with lock:print("I was study from {}, {}".format(name, ctime()))sleep(1)if __name__ == "__main__":# 定义1把线程锁lock = threading.Lock()# 通过threading库中的thread类,创建你线程,指定线程的执行函数,通过args元组传递参数t1 = threading.Thread(target=music, args=("<nice day>", lock))t2 = threading.Thread(target=study, args=("<game>", lock))# 启动线程t1.start()t2.start()# 阻塞等待回收资源t1.join()t2.join()print("now time: {}".format(ctime()))

3.多进程

多线程无法利用多核优势,如果想要充分使用多核CPU的资源,就需要使用到多进程,python提供了multiprocessing模块,与线程不同,进程没有任何共享状态,进程修改的数据,改动仅限该进程内,也就是相对独立。

通过库中的Process类就可以创建进程,并且指定进程的执行函数

同样也有进程锁,multiprocessing.lock()

from time import ctime, sleep# 导入进程类
from multiprocessing import Process
def music(name):for i in range(5):print("I was listening to music <{}>,{}".format(name, ctime()))sleep(1)
def study(name):for i in range(5):print("I was study from {}, {}".format(name, ctime()))sleep(1)if __name__ == "__main__":# 通过库中的process类,创建多进程,指定进程的执行函数,通过args元组传递参数t1 = Process(target=music, args=("<nice day>", ))t2 = Process(target=study, args=("<game>", ))# 启动进程t1.start()t2.start()# 阻塞等待回收资源t1.join()t2.join()print("now time: {}".format(ctime()))

四.多进程间通信

1.管道

pipe()返回两个连接对象代表pipe的读写两端,每个连接对象都有send()方法和recv()方法。但是如果两个进程或线程对象同时读取或写入管道两端的数据时,管道中的数据有可能会所坏,所以只能同时各使用读端写端。

from time import ctime, sleep
from multiprocessing import Process, Pipedef music(name, wfd):for i in range(5):# send()函数可以将任意类型的数据发送到读端wfd.send("ssssssssss")def study(name, rfd):for i in range(5):# recv()函数从读端读取数据,读端无数据就阻塞等待str1 = rfd.recv()print("recv: ", str1)if __name__ == "__main__":# 创建管道,得到读端和写端rfd, wfd = Pipe()t1 = Process(target=music, args=("<nice day>", wfd))t2 = Process(target=study, args=("<game>", rfd))t1.start()t2.start()t1.join()t2.join()print("now time: {}".format(ctime()))

2.消息队列

在内存中开辟一个队列模型,用来存放消息,任何拥有队列对象的进程都可以进行消息的存取和取出。

from time import ctime, sleep
from multiprocessing import Process, Queuedef music(name, q):for i in range(5):sleep(2)q.put("hello")q.put([1, 2, 3, 4])def study(name, q):for i in range(5):str1 = q.get()print("recv: ", str1)if __name__ == "__main__":# 创建消息队列q = Queue()t1 = Process(target=music, args=("<nice day>", q))t2 = Process(target=study, args=("<game>", q))t1.start()t2.start()t1.join()t2.join()print("now time: {}".format(ctime()))

3.共享内存

有两种方法:value将一个值存放在内存中;array:将多个数据存放在内存中但要求数据类型一致

from multiprocessing import Process,Value,Array
from time import sleep
def funV(mem):mem.Value = 200 # 修改共享内存值
def funC(mem, n):for i in range(n):mem[i] = i   #修改共享内存值if __name__ == "__main__":mem0 = Value('i', 100)mem1 = Array('i', 6)p1 = Process(target=funV, args=(mem0,))p1.start()p2 = Process(target=funC, args=(mem1, 6))p2.start()sleep(2)print(mem0.value)for i in mem1:print(i)p1.join()p2.join()

五.python网络编程

网络通信架构主要有下面两种:

C/S架构:

client/server,典型的两层架构,整个过程就是客户端服务器架构,客户端包含一个或多个运行在用户的计算机程序上,服务器有两个分别是数据库服务器和Socket服务器,数据库服务器主要是通过数据库连接客户端访问服务器端数据,Socket服务器是用于与客户端通信的

B/S架构:

Browser与Server,是浏览器/服务器架构,浏览器指的是web浏览器主要处理少数业务逻辑,服务器的主要作用是处理业务逻辑,由三层架构组成,它的使用简单不需要安装直接可在web浏览器中运行,此架构的客户端包含的逻辑少

通信端点与套接字

        在服务器响应客户端的请求之前,双方需要进行一系列的准备工作,首先要创建一个通信端点,服务器通过该通信端点监听客户端的请求,我们常用的一种通信端点为套接字,在通信开始之前,网络应用程序都需要创建套接字。

在socket通信中我们通过主机-端口对找到通信的对象,通过url或ip地址,我们可以在互联网中找到特定的主机,主机的不同的端口号有不同的通信作用,只要确定主机和端口号,就可在网络中确认唯一一个通信端点(端口号范围0-65535,小于1024的端口预留给了系统)

在之前的高级编程中我们已经讲过套接字编程的主要流程,这里也就不再赘述,用一张图简单概括一下:

 socket套接字接口总结:

1.tcp服务器编程步骤

和我们在c高级编程中学习的步骤一样,在python中更加简化了一些操作,但总体步骤还是一样,首先将socket模块引入,随后创建套接字,绑定端口ip,监听,等待连接收发消息,这里我们就简单将tcp服务器的代码示例展示出来,tcp客户端,udp服务器和客户端的代码这里就不再进行展示了。直接将代码图片贴在下方

import socket# 创建基于IPV4协议的tcp套接字,定义socket类型的对象
sockfd = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)# 设置端口重用
sockfd.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)# 绑定本地ip和端口
sockfd.bind(("0.0.0.0", 9522))# 设置监听套接字
sockfd.listen(5)while True:print("wait for a client")# 阻塞等待,接受客户端连接connfd, addr = sockfd.accept()print("client: ", addr)while True:# recv() 返回bytes类型str1 = connfd.recv(12)print("recv: ", str1)str2 = "hello"# send():将数据发送出去,参数必须为bytes,所以要对str进行编码->bytesconnfd.send(str2.encode())if str1.decode() == "quit":break

2.tcp客户端

3.udp服务器和客户端

4.socketsever

上面的编程流程还是过于复杂了一点,于是python给我们提供了一个简化后的高级模块,我们只需要从这个模板中引入相应的类就可以直接跳过上面创建,绑定,监听等操作,只需要继承对应的类然后重写handler方法,当客户端连接成功后,系统便自动调用该函数处理客户端请求

# 从socketserver模块导入Tcpserver服务器类,StreamRequestHandler处理客户端请求的类
from socketserver import TCPServer, StreamRequestHandler, ThreadingTCPServer# 该封装好的TCP服务器处理客户端请求,必须由StreamRequsetHandler类中的handler方法进行处理
class MyRequest(StreamRequestHandler):# 重写基类的handler方法,当客户端连接成功,系统会自动调用该函数处理客户端请求# 当handler函数处理完客户端的数据请求,会自动断开与客户端的连接def handle(self) -> None:while True:buf = self.request.recv(1024)print(buf)if buf.decode() == "quit":breakif __name__ == "__main__":# 创建Tcpsever对象,就是在创建tcp的服务器,指定服务器地址和处理请求的类# server = TCPServer(("0.0.0.0", 8888), MyRequest)# 创建基于TCP的多线程服务器server = ThreadingTCPServer(("127.0.0.1", 8888), MyRequest)print("server init success")# 让服务器永久阻塞等待新的客户端连接,当客户端的请求处理完成,会自动断开与客户端的连接server.serve_forever()

5.httpserver

上面我们介绍的都是C/S架构下的,下面我们简单介绍一下B/S架构下的服务器编程

# 从http包中的server模块中导入,httpserver服务器类和CGIHTTPRequesthandler处理浏览器请求类
from http.server import HTTPServer, CGIHTTPRequestHandler
# 如果要定制自己的网页http服务器,那么必须设计类继承于CGIHTTPRequesthandler,重写run_cpiif __name__ == "__main__":http = HTTPServer(("", 8888), CGIHTTPRequestHandler)print("starting: ", str(http.server_port))http.serve_forever()

六.python串口编程

串口通信是指外设和计算机间,通过数据信号线,地线,控制线等,按位进行传输数据的一种通信方式,这种方式使用的数据线少,在远距离通信中可以节约通信成本,但是传输速度比并行传输低,是一种非常通用的设备通信协议,python中的pyserial模块封装了python对串口的访问。

import serial.tools.list_ports
import serialdef findValiable():ports = list(serial.tools.list_ports.comports())if len(ports) == 0:print("系统无可用串口")else:for i in ports:print(ports)if __name__ == "__main__":# findValiable()# 创建串口对象,设置串口协议s = serial.Serial(port="COM1", baudrate=115200)while True:# 指定读取数据,那么只有数据满足读取的字节数,read函数才能返回list1 = s.read(20)print("recv: ", list1)# readlines(0):读取0行,表示不读数据,参数不为0,就可以读取所有数据# list1 = s.readlines(1)#for i in list1:#    print(i.decode(), end="")# buf = input("请输入:")# s.write(buf.encode())# s.flush()

python学习笔记:python类和对象,文件操作,网络编程相关推荐

  1. Python学习笔记 (类与对象)

    Python学习笔记 (类与对象) 1.类与对象 面向对象编程语言类: 一个模板, (人类)-是一个抽象的, 没有实体的对象: (eg: 张三, 李四) 属性: (表示这类东西的特征, 眼睛, 嘴巴, ...

  2. python学习笔记:第8天 文件操作

    目录 1. 文件操作介绍 2. 文件操作的几种方式 3. 文件的操作的方法 1. 文件操作介绍 说到操作文件我们肯定会想到流,文件的操作都是通过流来操作的.在python中文件的操作非常简单,并不像J ...

  3. python面向对象编程72讲_2020-07-22 Python学习笔记27类和面向对象编程

    一些关于自己学习Python的经历的内容,遇到的问题和思考等,方便以后查询和复习. 声明:本人学习是在扇贝编程通过网络学习的,相关的知识.案例来源于扇贝编程.如果使用请说明来源. 第27关 类与面向对 ...

  4. Python 学习笔记 系统模块 面对对象思想 类和对象 类中的方法与对象 构造函数和析构函数

    一.系统模块: UTC:国际标准时间, 格林尼治天文时间,UTC+8 时间戳:指定时间距离1970.1.1 00:00:00的秒数 time:时间 datetime:日期 calendar:万年历 o ...

  5. Python学习笔记:类

    本文来自:入门指南 开胃菜参考:开胃菜 使用Python解释器:使用Python解释器 本文对Python的简介:Python 简介 Python流程介绍:深入Python 流程 Python数据结构 ...

  6. Python学习笔记--day09 函数 模块 (文件、路径操作)

    第二模块 函数&模块 第一模块主要是学习python基础知识,从第二模块开始就可以通过程序去解决工作中实际的问题. 函数,一个用于专门实现某个功能的代码块(可重用). 内置函数 len.bin ...

  7. python学习之【类和对象】

    前言 五一快乐! 上一篇文章python学习--[第八弹]中,给大家介绍了python中的函数,这篇文章接着学习python中的类和对象. 我们知道,python中一切皆对象.在这篇文章开始之前,我们 ...

  8. python学习day2:类与对象

    类与对象 一.内置函数 1.int方法 (1)def__add__(self, y):       #两数相加 """ x.__add__(y) <==> x ...

  9. python学习笔记(面向对象,类)

    一.类的定义 1.类的基本结构 #命名规则: 驼峰法 class Student(): # 使用class 定义类a= 1 # 变量name = '小明'def aa(self): # 函数print ...

  10. python学习笔记:类的方法总结

    python中类的方法总结 在python中,类的方法有如下三种: (1)实例方法(即:对象方法) (2)类方法 (3)静态方法 下面,将对这三种方法进行总结. 1.实例方法(对象方法) 通常情况下, ...

最新文章

  1. 声音大小对于测距数值的影响
  2. ios 上运行linux,你可曾想过在iOS上跑Linux?
  3. Spark1.2新特性概述
  4. python安装第三方包总是超时_(python pip安装第三方库超时问题(raise ReadTimeoutErrorself._pool, None, 'Read timed out.')...
  5. [软件工程学习笔记]浅谈敏捷开发
  6. 配置监控中心-及管理平台
  7. Linux Hugetlbfs内核源码简析-----(二)Hugetlbfs挂载
  8. Oracle查询 rownum和rowid的区别
  9. BZOJ_2243 [SDOI2011]染色 【树链剖分+线段树】
  10. 卸载注册表_3Dmax软件无法安装?3Dmax软件正确卸载方法,重装无忧
  11. 一信通短信接口对接_吉信通:短信接口是什么?
  12. 旭荣管理软件怎么修改小票内容_水果门店管理系统怎么管理水果门店的
  13. Thinkphp 模板/包含文件
  14. Observable与Subject
  15. Android 桌面小部件的背景透明度及颜色的动态实现
  16. 超长指令字计算机,超长指令字
  17. p30升级鸿蒙能支持5G网络吗,华为p30支持5g吗
  18. python计算机视觉-1.2.2 图像轮廓与直方图
  19. RAKE(快速自动关键字抽取)算法原理与实现
  20. 网络Socket编程

热门文章

  1. win10无法修改默认浏览器(闪退)解决方法//修改默认浏览器闪退解决办法,亲测实用有效!!!!////默认浏览器被修改成Internet explorer了,想修改发现改不了,一点就闪退
  2. Joomla安装及使用
  3. gc java ibm_gc分析工具(IBM Pattern Modeling and Analysis Tool for Java Garbage Collector)
  4. 思维导图02----Java面向对象
  5. 从A股暴跌看中国经济发展
  6. python 入门一(数据结构)
  7. 升级了鸿蒙资料还在吗,手机升级更新鸿蒙系统会清空数据吗?华为鸿蒙升级需要备份吗...
  8. lua和go混合调用调试记录支持跨平台(通过C和LuaJit进行实现)
  9. 关于tableau里面展示部分时间段的问题简述
  10. 网页如何打开php文件怎么打开,PHP文件怎么打开?打开PHP文件方法介绍