python基础进阶
一、基础
1. import xxx和from xxx import yyy
import Module #引入模块
from Module import Other #引入模块中的类、函数或者变量
from Module import * #引入模块中的所有'公开'成员
# 导入整个datetime包
import datetime
print(datetime.datetime.now())
# 导入datetime包里的datetime类
from datetime import datetime
print(datetime.now())
# import之后前者是datetime这个包可见,后者是datetime.datetime这个类可见
import或from-import只能导入一个模块(.py)或者导入一个函数或者类,不能导入一个文件夹。
import:解释器执行到import语句, 如果在搜索路径中找到了指定的模块, 就会加载它。该过程遵循LEGB作用域原则, 如果在一个模块的顶层导入, 那么它的作用域就是全局的;如果在函数中导入,那么它的作用域是局部的。 如果模块是被第一次导入, 它将被加载并执行。
from-import:导入指定的模块属性, 也就是把指定名称导入到当前作用域。容易破坏命名空间,如果使用from导入变量,变量碰巧和作用域中现有变量重名,变量就会被悄悄的覆盖掉。
在实践中, "from module import *" 不是良好的编程风格,如果使用from导入变量,且那些变量碰巧和作用域中现有变量同名,那么变量名就会被悄悄覆盖掉。使用import语句的时候就不会发生这种问题,因为我们是通过模块名才获取的变量名,像module.attr不会和现有作用域的attr冲突。
我们只在两种场合下建议使用这样的方法, 一个场合是:目标模块中的属性非常多, 反复键入模块名很不方便 , 例如 Tkinter (Python/Tk) 和 NumPy (Numeric Python) 模块 , 可能还有 socket 模块。另一个场合是在交互解释器下, 因为这样可以减少输入次数。
2. 导入子目录模块
- hello.py
- base_unit
-- __init__.py
-- person.py
person.py
#!/usr/bin/env python
import time
class Person:
def __init__(self):
self.no = 1234
self.age = 20
print("Person %d output start" % self.no)
time.sleep(1)
print("Person %d output end" % self.no)
def get_age(self):
return self.age
if __name__ == "__main__":
Person()
hello.py
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import base_unit.person
#from base_unit import person
p=base_unit.person.Person()
print(p.age, p.no)
print(p.get_age())
导入子目录模块必须使用import base_unit.person或from base_unit import person,而不能使用import base_unit(报错,不能导入文件夹)。
当使用import base_unit.person时,调用person对象,也必须使用base_unit.person前缀。
3. 查找python包位置
1)modele.path变量
2)module.__file__变量
3)环境变量PYTHONPATH
4)pip show package
3. 单引号、双引号、三引号区别
Python中的单引号'',双引号"",三引号""" """,或者''' ''' 都可以用来包含字符串,没有任何区别。
三引号包含的字符串可由多行组成,一般可表示大段的叙述性字符串。特例,多行注释用三个单引号(""")或三个双引号(""")。
在使用时基本没有差别,但双引号和三引号("""...""")中可以包含单引号,三引号('''...''')可以包含双引号,而不需要转义。单引号也可包含双引号而不需要转义。
4. r'',b'',u'',f''含义
字符串前加r,去除转义字符。s=r'ABC\-001'是‘ABC\-001'
字符串前加f,字符串中支持大括号内的python表达式。
import time
t0 = time.time()
time.sleep(1)
name = 'processing'
print(f'{name} done in {time.time() - t0:.2f} s')
----------
processing done in 1.00 s
字符串前加b,后面的字符串是bytes类型。例如,网络编程中,服务器和浏览器只认bytes类型数据。如send函数的参数和recv函数的返回值都是bytes类型。
# python3中,bytes和str互相转换方式
str.encode('utf-8')
bytes.decode('utf-8')
字符串前加u,后面的字符串以Unicode格式编码,一般用在中文字符串前面,防止因为源码存储格式问题,导致再次使用时出现乱码。
5. 变量后冒号
变量名后面的冒号是类型注解,3.6以后加入的,冒号右边是类型,仅仅是注释,方便帮助复杂案例中的类型推断。类型注释只是一种提示,并非强制的,Python解释器不会去校验value的类型是否真的是type。
var: type = value
# 本质就是var = value # type就是var期望的类型
6. 函数声明后箭头
声明函数后箭头:"->" 是返回值的注释,-> str 意思即是提醒函数使用者返回值会是一个str型。
def f(ham: str, eggs: str = 'eggs') -> str :
print("Annotations:", f.__annotations__)
print("Arguments:", ham, eggs)
return ham + ' and ' + eggs
print(f("test","abc"))
在官方文档指明.__annotations__是函数的参数注释和返回值注释:
所以打印出Annotations: {'ham': <class 'str'>, 'eggs': <class 'str'>, 'return': <class 'str'>}
7. 三目运算符(if else在同一行)
a = 'abc'
b = a if a == 'abc' else 'cba'
二、对象编程
参考:面向对象编程 - 廖雪峰
class Student(object):
def __init__(self, name, score):
self.name = name ##实例属性
self.score = score
number = 0 ## 类属性
def print_score(self):
print('%s: %s' % (self.name, self.score))
在Python中,定义类是通过class关键字。class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的。
2.1 构造函数
通过定义一个特殊的__init__方法,在创建实例的时候,就把name,score等属性绑上去。
注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。
有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传。
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且调用时,不用传递该参数。
注:和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同。
2.2 实例化
创建类实例是通过类名+()实现的:
bart = Student()
>>> class Student():
... pass
...
>>> bart = Student()
>>> bart
<__main__.Student object at 0x7fd466495c10>
2.3 数据封装
要让内部属性不被外部访问,可以把属性的名称前加上两个下划线(__)。在Python中,实例的变量名如果以两个下划线(__)开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。
在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__、__score__这样的变量名。
有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
由于Python是动态语言,根据类创建的实例可以任意绑定属性。给实例绑定属性的方法是通过实例变量,或者通过self变量。
2.4 类属性
直接在class中定义属性,这种属性是类属性,归类所有,但类的所有实例都可以访问到。
在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。
实例属性属于各个实例所有,互不干扰;类属性属于类所有,所有实例共享一个属性。
三、时间
time
import time
print(time.time())
print(time.time_ns())
datetime
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import datetime
i = datetime.datetime.now()
print ("当前的日期和时间是 %s" % i)
print ("ISO格式的日期和时间是 %s" % i.isoformat())
print ("dd/mm/yyyyThh:mm:ss 格式是 %s/%s/%sT%s:%s:%s" % (i.day, i.month, i.year, i.hour, i.minute, i.second))
当前的日期和时间是 2022-01-06 19:16:53.150451
ISO格式的日期和时间是 2022-01-06T19:16:53.150451
dd/mm/yyyyThh:mm:ss 格式是 6/1/2022T19:16:53
定时任务框架apscheduler
参考:Python定时任务框架apscheduler - csdn
四、二进制处理
五、文件
读取大文件
def readlines(f, separator):
'''
读取大文件方法
:param f: 文件句柄
:param separator: 每一行的分隔符
:return:
'''
buf = ''
while True:
while separator in buf:
position = buf.index(separator) # 分隔符的位置
yield buf[:position] # 切片, 从开始位置到分隔符位置
buf = buf[position + len(separator):] # 再切片,将yield的数据切掉,保留剩下的数据
chunk = f.read(4096) # 一次读取4096的数据到buf中
if not chunk: # 如果没有读到数据
yield buf # 返回buf中的数据
break # 结束
buf += chunk # 如果read有数据 ,将read到的数据加入到buf中
with open('text.txt',encoding='utf-8') as f:
for line in readlines(f,'|||'):
# 为什么readlines函数能够使用for循环遍历呢, 因为这个函数里面有yield关键字呀, 有它就是一个生成器函数 ......
print(line)
串口
import time
import serial
import datetime
import serial.tools.list_ports
port_list = list(serial.tools.list_ports.comports())
print(port_list)
for i in range(0, len(port_list)):
print(port_list[i])
port = "/dev/ttyS1"
bps = 115200
ser = serial.Serial(port, bps, timeout=0.1)
print(ser)
time_fix = False
msg = ""
with open("/home/robot/logs/rcu_com.log", "a") as fp:
while True:
if ser.in_waiting:
try:
msg = ser.read(ser.in_waiting).decode()
except:
pass
if "\n" in msg:
data_list = msg.strip().split("\n")
for i in range(len(data_list)):
if data_list[i] != "":
fp.write(data_list[i] + "\n")
dt_ms = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
fp.write(dt_ms + " ")
elif msg.strip() is "\n":
continue
else:
fp.write(msg)
time.sleep(0.1)
六、基于函数的生成器
如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。
生成器返回yield的右值,当下次调用生成器时,yield左边等号的左值等于yield的返回值。初始yield返回Node,之后可以通过send()发送返回值。
>>> def test(val=0):
while True:
y = yield val
print(y)
>>> t = test() # 启用生成器
>>> next(t)
0
>>> next(t)
None
0
为什么第二次Next调用后,print(y)输出了None,而不是 0 ?因为赋值语句从等号右边开始。
第一次Next调用后,执行等号右边的表达式 yield val,执行完后函数暂停运行,赋值操作根本没有被执行。
当第二次再运行时才执行赋值(等号左半部分),而生成器恢复运行时yield初始值为None,所以 y = None。
>>> def test(val=0):
while True:
y = yield val
val = y
>>> t = test()
>>> next(t)
0
>>> t.send('Hello,world')
'Hello,world'
send方法包含与next同样的效果,但还能为生成器传值。
send方法与next的不同在于: send首先给生成器传值,再执行和Next相同的操作( 从上次暂停的地方开始,执行到下一个yield ),send(None) 等价于 Next。
所以为什么第一次运行生成器send参数只能为None?
Because generator-iterators begin execution at the top of the generator's function body,
there is no yield expression to receive a value when the generator has just been created.
Therefore, calling send() with a non-None argument is prohibited when the generator iterator has just started,
and a TypeError is raised if this occurs (presumably due to a logic error of some kind).
Thus, before you can communicate with a coroutine you must first call next() or send(None) to advance its execution to the first yield expression.
生产者消费之协程示例,参考廖雪峰协程
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('[CONSUMER] Consuming %s...' % n)
r = '200 OK'
def produce(c):
c.send(None)
n = 0
while n < 2:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
print('[PRODUCER] Consumer return: %s' % r)
c.close()
c = consumer()
produce(c)
[PRODUCER] Producing 1...
[CONSUMER] Consuming 1...
[PRODUCER] Consumer return: 200 OK
[PRODUCER] Producing 2...
[CONSUMER] Consuming 2...
[PRODUCER] Consumer return: 200 OK
七、virtualenv
virtualenv 是一个创建隔绝的Python环境的工具。virtualenv创建一个包含所有必要的可执行文件的文件夹,用来使用Python工程所需的包。
pip install virtualenv
cd my_project_dir
virtualenv venv #venv为虚拟环境目录名,自定义
virtualenv -p /usr/bin/python2.7 venv # -p参数指定Python解释器程序路径
source venv/bin/activate # 激活虚拟环境
venv/bin/deactivate # 回到系统默认的Python解释器
pip install pylint #安装python静态检查器
rm -rf venv # 删除虚拟环境
vscode配置
vscode左下方有Python解释器版本,直接点击后出现“Select Interpreter”选择合适python解释器即可。