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

四、二进制处理

Python二进制数据处理

五、文件

读取大文件

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解释器即可。
posted @ 2018-05-12 15:52  yuxi_o  阅读(933)  评论(0编辑  收藏  举报