内容概要

  1. 特殊成员
  2. 反射
  3. configparser模块
  4. hashlib模块
  5. logging模块
  6. 异常处理
  7. 模块

1. 特殊成员

什么是特殊成员呢? __init_()就是个特殊的成员. 带双下划线的都是特殊方法. 这些方法在特殊的场景的时候会被自动的执行. 比如
  1. 类名() 会自动执行__init__()
  2. 对象() 会自动执行__call__()
  3. 对象[key] 会自动执行__getitem__()
  4. 对象[key] = value 会自动执行__setitem__()
  5. del 对象[key] 会自动执行 __delitem__()
  6. 对象+对象 会自动执行 __add__()
  7. with 对象 as 变量 会自动执行__enter__ 和__exit__
  8. 打印对象的时候 会自动执行 __str__
  9. 干掉可哈希 __hash__ == None 对象就不可哈希了.

创建对象的真正步骤:

首先, 在执行类名()的时候. 系统会自动先执行__new__()来开辟内存. 此时新开辟出来的内存区域是空的. 紧随其后, 系统自动调用__init__()来完成对象的初始化⼯作. 按照时间轴来算.

  1. 加载类
  2. 开辟内存(__new__)
  3. 初始化(__init__)
  4. 使用对象干xxx

类似的操作还有很多很多. 我们不需要完全刻意的去把所有的特殊成员全都记住. 实战中也用不到那么多. 用到了查就是了.

单例模式:

class Foo(object):

    _instance = None # 实例

    # 先
def __new__(cls, *args, **kwargs):
if Foo._instance == None:
Foo._instance = object.__new__(cls) # 开辟内存
return Foo._instance # 后
def __init__(self):
print("我是一个简单的__init__") f1 = Foo() # 第一步先执行__new__分配内存, 第二步执行__init__初始化这段内存
f2 = Foo()
print(f1)
print(f2)

2. 反射

python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

关于反射, 一共有4个函数:
  1. hasattr(obj, str)        判断obj中是否包含str成员
  2. getattr(obj,str)          从obj中获取str成员
  3. setattr(obj, str, value)      把obj中的str成员设置成value. 注意. 这里的value可以是值, 也可以是函数或者方法
  4. delattr(obj, str)         把obj中的str成员删除掉

注意, 以上操作都是在内存中进行的. 并不会影响源代码

四个方法的使用示例:

class Foo:
f = "类的静态变量"
def __init__(self,name,age):
self.name = name
self.age = age def func1(self):
print("hi, %s" %self.name) obj = Foo("zhouxingxing",) # 拿着功能的名字,去对象或者模块里找对应的功能
print(hasattr(obj,'name')) # True 存在返回True
print(hasattr(obj,'func1')) # True
print(hasattr(obj,'gender')) # False 不存在返回False # 获取属性
# getattr(object, name, default=None)
n = getattr(obj,"name")
print(n)
# lishichao func = getattr(obj,"func1")
func()
# hi, lishichao # print(getattr(obj,"aaaa"))
# AttributeError: 'Foo' object has no attribute 'aaaa' 报错 # 第三个参数:默认值,如果该属性不存在,则使用默认值
print(getattr(obj,"aaaa","不存在啊"))
# 不存在啊 # 设置属性
# setattr(object,name,value)
setattr(Foo,"f1","类的静态变量") # 设置类变量
setattr(obj,"sb",True) # 设置实例变量
setattr(obj,"show_name",lambda self:self.name+"sb") # 设置属性
print(obj.__dict__)
# {'name': 'lishichao', 'age': , 'sb': True, 'show_name': <function <lambda> at 0x00000000021E1730>}
print(obj.show_name(obj))
# zhouxingxingsb # 删除属性
delattr(obj,"age")
delattr(obj,"show_name")
# delattr(obj,"name111") #不存在,则报错。 AttributeError: name111
print(obj.__dict__)
# {'name': 'zhouxingxing', 'sb': True} 在内存中都被删掉了

反射当前模块成员:

import sys

def s1():
print('s1') def s2():
print('s2') this_mod = sys.modules[__name__] # <module '__main__' from 'E:/python-25期课上代码/day07/课上代码/02 反射.py'>
print(hasattr(this_mod,"s1"))
# True
func = getattr(this_mod,"s2")
func() # s2

导入其他模块,利用反射查找该模块是否存在某个方法

#!/usr/bin/env python3
# _*_ coding:utf- _*_ def t1():
print('from the t1') def t2():
print('from the t2') def t3():
print('from the t3') money = ""

master

import master
while :
# 根据用户输入的功能进行调用
tool = input("请输入你要执行的功能的名字:") # chi
# 拿着功能的名字,去对象或者模块里找对应的功能
# has 有 attr 属性
if hasattr(master, tool):
# 把这个功能拿出来
fn = getattr(master, tool)
if callable(fn): # 判断是否可以被调用
fn() # 调用函数
else:
print(fn) # 打印变量
else:
print("没有这个功能")

导入master

# 对象中的反射,类本身也是对象(python 一切皆对象)

class Foo(object):
staticField = "old boy" def __init__(self):
self.name = 'wupeiqi' def func(self):
return 'func' @staticmethod
def bar():
return 'bar' obj = Foo()
print(getattr(Foo, 'staticField'))
func1 = getattr(Foo, 'func')
print(func1(obj))
func2 = getattr(Foo, 'bar')
print(func2())

3. configparser模块

  该模块适用于配置文件件的格式与windows ini文件类似,可以包含一个或多个节(section)每个节可以有多个参数(键=值). 首先, 我们先看一个xxx服务器的配置文件

[DEFAULT]
ServerAliveInterval =
Compression = yes
CompressionLevel =
ForwardX11 = yes [server]
User = hg
Bind = 0.0.0.0
Port = [client]
IP = 127.0.0.0
Port =
ForwardX11 = no

我们用configparser就可以对这样的文件进行处理.首先, 是初始化

# . 初始化
import configparser
config = configparser.ConfigParser() config["DEFAULT"] = {
"sleep": ,
"session-time-out": ,
"user-alive":
} config["TEST-DB"] = {
"db_ip": "192.168.17.189",
"port": "",
"u_name": "root",
"u_pwd": ""
} config['168-DB'] = {
"db_ip": "152.163.18.168",
"port": "",
"u_name": "root",
"u_pwd": ""
} f = open("config.ini",mode="w")
config.write(f) # 写入文件
f.flush()
f.close() # 读取文件信息:
config = configparser.ConfigParser() config.read("config.ini") # 读取文件
print(config.sections()) # 获取到所有section(章节)。 [DEFAULT]是默认信息,每个章节都有[DEFAULT]中的信息
# 执行结果: ['TEST-DB', '168-DB'] # 从章节中获取到任意的数据,get 方法Section下的key对应的value
print(config.get("TEST-DB","db_ip"))
# 执行结果: 192.168.17.189 # 也可以像字典一样操作
print(config["TEST-DB"]['db_ip'])
print(config["168-DB"]["db_ip"])
# 执行结果:
# 192.168.17.189
# 152.163.18.168 # 循环TEST-DB章节下的所有key
for k in config["TEST-DB"]:
print(k) # 所有key:value
for k,v in config["TEST-DB"].items():
print(k,v) print(config.options("TEST-DB")) # 同for循环,找到 "TEST-DB"下所有键
# ['db_ip', 'port', 'u_name', 'u_pwd', 'sleep', 'session-time-out', 'user-alive'] print(config.items("TEST-DB")) # 找到 "TEST-DB"下所有键值对
# [('sleep', ''), ('session-time-out', ''), ('user-alive', ''), ('db_ip', '192.168.17.189'), ('port', ''), ('u_name', 'root'), ('u_pwd', '')]

增删改操作:

# 先读取. 然后修改. 最后写回文件
config = configparser.ConfigParser()
config.read("config.ini") # 读取文件 # 添加一个章节
config.add_section("172-DB")
config["172-DB"] = {
"db_ip": "172.168.12.11",
"port": "",
"u_name": "root",
"u_pwd": ""
} # 修改信息
config.set("168-DB","db_ip","10.0.3.26") # 删除章节
config.remove_section("TEST-DB") # 删除元素信息
config.remove_option("168-DB","db_ip") # 写回文件
config.write(open("config.ini",mode="w"))

4. MD5加密

  MD5是一种不可逆的加密算法. 它是可靠的. 并且安全的. 在python中我们不需要手写这一套算法. 只需要引入hashlib模块就能搞定MD5的加密工作

import hashlib
# . 创建对象
obj = hashlib.md5()
# . 把要加密的内容写入对象
obj.update("lishichao".encode("utf-8")) # 加密的必须是字节
# . 获取到MD5
miwen = obj.hexdigest()
print(miwen)
# 5f71293582408cc955d1a41fc434d29a

那这样的密文安全么? 其实是不安全的. 当我们用这样的密文去找一个所谓的MD5解密工具. 是有可能解密成功的.

  MD5不是不可逆么? 注意. 这里有一个叫撞库的问题. 就是. 由于MD5的原始算法已经存在很久了. 那就有一些人用一些简单的排列组合来计算MD5. 然后当出现相同的MD5密文的时候就很容易反推出原来的数据是什么. 所以并不是MD5可逆,而是有些别有用心的人把MD5的常见数据已经算完并保留起来了.
那如何应对呢? 加盐就行了. 在使用MD5的时候. 给函数的参数传递一个byte即可.

import hashlib
# . 创建对象,. 加盐
obj = hashlib.md5(b"asjdkanoiwhdwbiohjixvzx")
# . 把要加密的内容写入对象
obj.update("lishichao".encode("utf-8")) # 加密的必须是字节
# . 获取到MD5
miwen = obj.hexdigest()
print(miwen)
# 4404442131473d354913e82270dad0a1

md5的应用:

def my_md5(s):
obj = hashlib.md5(b"asjdkanoiwhdwbiohjixvzx")
obj.update(s.encode("utf-8"))
miwen = obj.hexdigest()
return miwen username = ""
password = "" # 用户登录
def login():
uname = input("请输入用户名:")
upwd = input("请输入密码:")
if uname == username and my_md5(upwd) == password:
print("登录成功")
else:
print("登录失败") # 用户注册
def reg():
global username
global password
uname = input("请输入用户名:")
upwd = input("请输入密码:")
username = uname
password = my_md5(upwd) # 密码放密文 reg()
login()

文件的MD5,用来校验文件是否传输完整:

obj = hashlib.md5(b"asjdkanoiwhdwbiohjixvzx")

# 文件获取MD5值
f = open("test.txt",mode="r",encoding="utf-8")
for i in f:
obj.update(i.encode("utf-8")) miwen = obj.hexdigest()
print(miwen)
# 5fcda51b94a2e76d0322881d457c5c4f # 修改文件内容后MD5值会有变化:
# be6e5639222ad8a950eaa98e7e66dc73

5. 日志

论日志的重要性,统计数据,报错信息,程序运行记录都由日志记录。在python中创建日志系统:
  1. 导入logging模块.
  2. 简单配置logging
  3. 出现异常的时候(except). 向日志中写错误信息.

# 对日志系统进行配置
# 单日志系统

 # filename: 文件名
# format: 数据的格式化输出. 最终在日志文件中的样子
# 时间-名称-级别-模块: 错误信息
# datefmt: 时间的格式
# level: 错误的级别权重, 当错误的级别权重大于等于leval的时候才会写入文件 logging.basicConfig(filename='x1.txt', # 日志文件名
# %(asctime) 时间
# %(name) root
# %(levelname)s 事件的严重性
# %(module)s 不用管
# %(message)
format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S', # 时间格式
level=) # 记录日志的最低级别 # 如何使用(重点)
logging.critical("我是critical 我最牛B") # 级别最高的日志
logging.error("我是error, 我第二牛B") # 程序员的错
logging.warning("我是warning, 警告") # 警告. 不一定会出错
logging.info("我是info, 普通日志") # 不是问题. 如果输出的量很大. 也很麻烦
logging.debug("比屁都小的事儿都记录") # 级别最低. 粒度最大
logging.log(, "皮炎平") # CRITICAL =
# FATAL = CRITICAL
# ERROR =
# WARNING =
# WARN = WARNING
# INFO =
# DEBUG =
# NOTSET =

# 多文件日志系统

# 创建一个操作日志的对象logger(依赖FileHandler)
file_handler1 = logging.FileHandler('l1.log', 'a', encoding='utf-8')
file_handler1.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s"))
logger1 = logging.Logger('汽车融资租赁', level=logging.ERROR)
logger1.addHandler(file_handler1) file_handler2 = logging.FileHandler('l2.log', 'a', encoding='utf-8')
file_handler2.setFormatter(logging.Formatter(fmt="%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s"))
logger2 = logging.Logger('IHOS医疗卫生综合服务系统', level=logging.ERROR)
logger2.addHandler(file_handler2) logger1.error("我出错了. 我的车找不到了")
logger2.error("医院丢了")

即输出到屏幕,又写入到文件

import logging
# 创建 logging 对象
logger = logging.getLogger() # 创建文件对象
fh1 = logging.FileHandler("a1.log",encoding="utf-8") # 创建屏幕对象
sh = logging.StreamHandler() # 日志格式
formater1 = logging.Formatter(
fmt='%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s', # 显示格式
datefmt='%Y-%m-%d %H:%M:%S',) formater2 = logging.Formatter(
fmt='%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s', # 显示格式
datefmt='%Y-%m-%d %H:%M:%S',) # 给对象绑定格式
fh1.setFormatter(formater1)
sh.setFormatter(formater2) # 给logger对象添加其他对象
logger.addHandler(fh1)
logger.addHandler(sh) # 设置logger级别
logger.setLevel() fh1.setLevel()
sh.setLevel() logging.debug('调试模式') #
logging.info('正常运行') #
logging.warning('警告') #
logging.error('错误') #
logging.critical('系统崩了') #

6. 异常处理

  什么是异常? 异常是程序在运行过程中产生的错误. 我们先制造一个错误. 来看看异常长什么样.

def chu(a, b):
return a/b
ret = chu(, )
print(ret) # 执行结果:
Traceback (most recent call last):
File "E:/python-25期课上代码/day07/课上代码/06 异常处理.py", line , in <module>
ret = chu(, )
File "E:/python-25期课上代码/day07/课上代码/06 异常处理.py", line , in chu
return a/b
ZeroDivisionError: division by zero

  什么错误呢. 除法中除数不能是0. 那如果真的出了这个错. 你把这一堆信息抛给客户么? 肯定不能. 那如何处理呢?

def chu(a, b):
return a/b try:
ret = chu(, )
print(ret)
except Exception as e:
print("除数不能是0") # 执行结果:
除数不能是0

  try ... except 是尝试着运行xxx代码. 出现了错误. 就执行except后面的代码. 在这个过程中. 当代码出现错误的时候. 系统会产生一个异常对象. 然后这个异常会向外抛. 被except拦截. 并把接收到的异常对象赋值给e. e就是异常对象.
  Exception 是所有异常的基类, 也就是异常的根. 换句话说. 所有的错误都是Exception的子类对象. 我们看到的ZeroDivisionError 其实就是Exception的子类.
  Exception 表示所有的错误. 太笼统了. 所有的错误都会被认为是Exception.当程序中出现多种错误的时候, 就不好分类了, 最好是出什么异常就用什么来处理.在try...execpt语句中.

还可以写更多的except:

try:
print("各种操作....")
except ZeroDivisionError as e:
print("除数不能是0")
except FileNotFoundError as e:
print("⽂件不存在")
except Exception as e:
print("其他错误")

  此时. 程序运行过程中. 如果出现了ZeroDivisionError就会被第一个except捕获. 如果出现了FileNotFountError就会被第二个except捕获. 如果都不是这两个异常. 那就会被最后的Exception捕获. 总之最后的Exception就是我们异常处理的最后一个守门员. 这时我们最常用的一套写法.

接下来. 给出一个完整的异常处理写法(语法):

try:
'''操作'''
except Exception as e:
'''异常的父类,可以捕获所有的异常'''
else:
'''保护不抛出异常的代码, 当try中无异常的时候执行'''
finally:
'''最后总是要执行我'''

  解读: 程序先执行操作, 然后如果出错了会走except中的代码. 如果不出错, 执行else中的代码. 不论处不出错. 最后都要执行finally中的语句. 一般我们用try...except就够用了. 顶多加上finally. finally一般用来作为收尾工作.

示例:

def chu(a, b):
return a/b try:
chu(, )
except Exception as e:
print("其他错误")
else:
print("没有异常,正常执行")
finally:
print("不管有没有异常,最后要执行我")
# 执行结果:
# 其他错误
# 不管有没有异常,最后要执行我 try:
chu(, )
except Exception as e:
print("其他错误")
else:
print("没有异常,正常执行")
finally:
print("不管有没有异常,最后要执行我")
# 执行结果:
# 没有异常,正常执行
# 不管有没有异常,最后要执行我

上面是处理异常. 我们在执行代码的过程中如果出现了一些条件上的不对等. 根本不符合我的代码逻辑. 比如. 参数. 我要求你传递一个数字. 你非得传递一个字符串. 那对不起. 我没办法帮你处理. 那如何通知你呢? 两个方案.
  方案一. 直接返回即可.
  方案二. 抛出一个异常.

第一种不够好,无法起到警示作用,所以直接抛一个错误出去. 那怎么抛呢? 我们要用到raise关键字

def add(a, b):
"""
求和运算
"""
if not type(a) == int and not type(b) == int:
# 当程序运行到这句话的时候. 整个函数的调用会被中断. 并向外抛出一个异常.
raise Exception("不是整数, 无法运算")
return a + b # 如果调用方不处理异常. 那产生的错误将会继续向外抛. 最后就抛给了用户
add("你好", "我叫赛利亚")
# Exception: 不是整数, 无法运算 # 如果调用方处理了异常. 那么错误就不会丢给用户. 程序也能正常进行⾏
try:
add("a", "b")
except Exception as e:
print("报错了.自己处理去吧")
# 报错了.自己处理去吧

  当程序运行到raise. 程序会被中断. 并实例化后面的异常对象. 抛给调用方. 如果调用方不处理. 则会把错误继续向上抛出. 最终抛给用户. 如果调用方处理了异常. 那程序可以正常的执行.

自定义异常:

  自己写的代码中出现了一个无法用现有的异常来解决问题的时候,需要自定义异常
  自定义异常: 写的类继承了Exception类. 那这个类就是一个异常类.

class GenderError(Exception):
pass class Person:
def __init__(self, name, gender):
self.name = name
self.gender = gender def Man(p): # 女
if p.gender != "男":
raise GenderError("进错了,这里是男澡堂") # 抛出异常
else:
print("欢迎光临") p1 = Person("alex", "男")
p2 = Person("景女神", "女") Man(p2) # 报错,程序就停了
Man(p1) # 处理异常
try:
Man(p2)
Man(p1)
except GenderError as e:
print(e) # e => 进错了,这里是男澡堂⼦
except Exception as e:
print("反正报错了")

示例

我们在调试的时候, 最好是能看到错误源自于哪里,需要引入另一个模块traceback. 这个模块可以获取到我们每个方法的调用信息.又被成为堆栈信息. 这个信息用来排错是很有帮助的.

import traceback

# 继承 Exception 就是异常类
class GenderError(Exception):
pass class Person:
def __init__(self,name,gender):
self.name = name
self.gender = gender def Man(person):
if person.gender != "男":
raise GenderError("性别不对") p1 = Person("周星星","男")
p2 = Person("张敏","女") # Man(p1)
# Man(p2) # 报错 会抛出异常: GenderError # 处理异常
try:
Man(p1)
Man(p2)
except GenderError as e:
val = traceback.format_exc() # 获取到堆栈信息
print(e)
print(val)
except Exception as e:
print("反正报错了")

执行结果:

当测试代码的时候把堆栈信息打印出来. 但是当到了线上的生产环境的时候把这个堆栈去掉即可.

异常信息记录日志:

import logging
import traceback logging.basicConfig(filename='error.log', # 日志文件名
# %(asctime) 时间
# %(name) root
# %(levelname)s 事件的严重性
# %(module)s 不用管
# %(message)
format='%(asctime)s - %(name)s - %(levelname)s - %(module)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S', # 时间格式
level=) # 记录日志的最低级别 try:
print(/)
except Exception:
logging.error(traceback.format_exc())

日志内容:

7. 模块

01.  模块
什么是模块. 模块就是一个包含了python定义和声明的文件, 文件名就是模块的名字加上.py后缀。我们写的py文件都可以看成是一个模块但是我们import加载的模块一共分成四个通用类别:
  1. 使用pyhton编写的py文件
  2. 已被变异为共享库或者DLL或C或者C++的扩展
  3. 包好组模块的包.
  4. 使用c编写并连接到python解释器的内置模块

为什么要使用模块? 为了我们写的代码可以重用. 不至于把所有的代码都写在一个文件内. 当项目规模比较大的时候. 就必须要把相关的功能进行分离. 方便维护和开发。

如何使用模块?导入模块有两种方式
1. import 模块
2. from xxx import xxx

02. import

示例文件:自定义模块my_module.py,文件名my_module.py,模块名my_module

# 自定义模快 my_module.py
# 示例文件:文件名my_module.py,模块名my_module
print('from the my_module.py') money= def read1():
print('my_module->read1->money',money) def read2():
print('my_module->read2 calling read1')
read1() def change():
global money
money=

my_module

引用 my_module 模块

# 导入模块
import my_module
print(my_module.money) # 使用模块中定义好的变量 my_module.read1() # 调用模块中的函数

在Python中模块是不能够重复导入的. 当重复导入模块时. 系统会根据 sys.modules 来判断该模块是否已经导入了. 如果已经导入. 则不会重复导入

import sys
print(sys.modules.keys()) # 查看导入的模块.
import my_module # 导入模块. 此时会默认执行该模块中的代码
import my_module # 该模块已经导入过了. 不会重复执行代码
import my_module
import my_module
import my_module
import my_module # 执行结果:
# dict_keys(['builtins', 'sys', '_frozen_importlib', '_imp', '_warnings', '_thread', '_weakref', '_frozen_importlib_external', '_io', 'marshal', 'nt', 'winreg', 'zipimport', 'encodings', 'codecs', '_codecs', 'encodings.aliases', 'encodings.utf_8', '_signal', '__main__', 'encodings.latin_1', 'io', 'abc', '_weakrefset', 'site', 'os', 'errno', 'stat', '_stat', 'ntpath', 'genericpath', 'os.path', '_collections_abc', '_sitebuiltins', 'sysconfig', 'sitecustomize'])
# from the my_module.py

导入模块的时候都做了些什么? 首先. 在导入模块的一瞬间. python解释器会先通过sys.modules来判断该模块是否已经导入了该模块. 如果已经导入了则不再导入. 如果该模块还未导入过. 则系统会做三件事.

  1. 为导入的模块创立新的名称空间
  2. 在新创建的名称空间中运行该模块中的代码
  3. 创建模块的名字. 并使用该名称作为该模块在当前模块中引用的名字.

我们可以使用globals来查看模块的名称空间

print(globals())
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000001DBB518>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:/python-25期课上代码/day07/课上代码/07 模块.py', '__cached__': None, 'sys': <module 'sys' (built-in)>, 'my_module': <module 'my_module' from 'E:\\python-25期课上代码\\day07\\课上代码\\my_module.py'>}

由于模块在导入的时候会创建其自己的名称空间. 所以. 我们在使用模块中的变量的时候一般是不会产生冲突的

import my_modlue
money =
print(my_module.money) # 模块中的变量
print(money) #自己的变量
# 执行结果:
#
#

为模块名起别名,相当于m1=1;m2=m1

import my_module as sm
print(sm.money)

在一行导入多个模块

import sys,os,re

模块搜索路径:

  python解释器在启动时会自动加载一些模块,可以使用sys.modules查看

  在第一次导入某个模块时(比如my_module),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用

  如果没有,解释器则会查找同名的内建模块,如果还没有找到就从sys.path给出的目录列表中依次寻找my_module.py文件。

  所以总结模块的查找顺序是:内存中已经加载的模块->内置模块->sys.path路径中包含的模块

# windows: python解释器模块搜索路径是项目根目录,和当前目录,通过 sys.path 查看模块搜索路径
# linux: 只有当前的目录,需要 sys.path.append() 把项目根目录添加进去

main是什么,main是程序的入口

我们可以通过模块的全局变量__name__来查看模块名:
当做脚本运行:
__name__ 等于'__main__' 当做模块导入:
__name__= 模块名 def main():
  pass if __name__ == "__main__": # 启动文件, 被当做模块导入时 不执行。
main()

正确的导入模块的顺序:
  1. 所有的模块导入都要写在最上⾯. 这是最基本的
  2. 先引入内置模块
  3. 再引入扩展模块
  4. 最后引入你自己定义的模块

03. from xxx import  xxx

在使用from的时候, python也会给我们的模块创建名称空间. 这一点和import是一样的. 但是from xxx import xxx的时候. 我们是把这个空间中的一些变量引入过来了. 说白了. 就是部分导入. 当这个模块中的内容过多的时候. 可以选择性的导入要使用的内容

from my_module import read1
read1()

此时是可以正常运行的. 但是我们省略了之前的模块.函数() 直接函数()就可以执行了, 并且from语句也支持一行语句导入多个内容.

from my_module import read1,read2,change
read1()
read2()
change()

同样支持as

from my_module import read1 as rd
rd()

from  xxx  import  *

是把模块中的所有内容都导入. 注意, 如果模块中没有写出__all__ 则默认所有内容都导入. 如果写了__all__ 此时导入的内容就是在__all__列表中列出来的所有名字.

# haha.py
__all__ = ["money", "chi"]
money = def chi():
print("我是吃")
def he():
print("我是呵呵") # test.py
from haha import *
chi()
print(money)
# he() # 报错

最后. 看一下from的坑. 当我们从一个模块中引入一个变量的时候. 如果当前文件中出现了重名的变量时. 会覆盖掉模块引入的那个变量.

from my_module import money
money =
print(money) #

  所以. 不要重名. 切记. 不要重名! 不仅仅是变量名不要重复. 我们自己创建的py文件的名字不要和系统内置的模块重名. 否则. 引入的模块都是python内置的模块.

8. 包

什么是包?

  包是一种通过 '.模块名' 来组织python模块名称空间的方式. 那什么样的东西是包呢? 我们创建的每个文件夹都可以被称之为包. 但是我们要注意, 在python2中规定. 包内必须存在__init__.py文件. 创建包的目的不是为了运行, 而是被导入使用. 包只是一种形式而已. 包的本质就是一种模块

为何要使包?

  包的本质就是一个文件夹, 那么文件夹唯一的功能就是将文件组织起来,随着功能越写越多, 我们无法将所有功能都放在一个文件中, 于是我们使用模块去组织功能,随着模块越来越多, 我们就需要用文件夹将模块文件组织起来, 以此来提高程序的结构性和可维护性

首先, 我们先创建一些包. 用来作为接下来的学习. 包很好创建. 只要是一个文件夹, 有__init__.py就可以

创建目录结构:

import os
os.makedirs('glance/api')
os.makedirs('glance/cmd')
os.makedirs('glance/db')
l = []
l.append(open('glance/__init__.py','w'))
l.append(open('glance/api/__init__.py','w'))
l.append(open('glance/api/policy.py','w'))
l.append(open('glance/api/versions.py','w'))
l.append(open('glance/cmd/__init__.py','w'))
l.append(open('glance/cmd/manage.py','w'))
l.append(open('glance/db/__init__.py','w'))
l.append(open('glance/db/models.py','w'))
map(lambda f:f.close() ,l)

#文件内容
#policy.py
def get():
print('from policy.py') #versions.py
def create_resource(conf):
print('from version.py: ',conf) #manage.py
def main():
print('from manage.py') #models.py
def register_models(engine):
print('from models.py: ',engine)

文件内容

包的导入:

  1.关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但都必须遵循这个原则。

  2.对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。

  3.对比import item 和from item import name的应用场景:
   如果我们想直接使用name那必须使用后者。

import ,在与包 glance 同级别的文件中测试,test.py文件。

import glance.db.models
glance.db.models.register_models("mysql")

from ... import ...

需要注意的是from后import导入的模块,必须是明确的一个不能带点,否则会有语法错误,如:from a import b.c是错误语法

还是 test.py 与 glance 目录同级

from glance.db import models
models.register_models("mysql") from glance.db.models import register_models
register_models("redis")

__init__ 文件

  不管是哪种方式,只要是第一次导入包或者是包的任何其他部分,都会依次执行包下的__init__.py文件(我们可以在每个包的文件内都打印一行内容来验证一下),这个文件可以为空,但是也可以存放一些初始化包的代码。

from  glance.api  import  *

在讲模块时,我们已经讨论过了从一个模块内导入所有*,此处我们研究从一个包导入所有*。

此处是想从包api中导入所有,实际上该语句只会导入包api下 __init__. py文件中定义的名字,我们可以在这个文件中定义__all___:

#在__init__.py中定义
print("我是api包下的__init__.py文件")
x= def func():
print('from api.__init.py') __all__=['x','func','policy']

api目录下的__init.py

此时我们在于 glance 同级的 test.py 文件中执行 from glance.api import * 就导入__all__中的内容(versions仍然不能导入)。

from glance.api import *
policy.get()
print(x)
func()
versions.create_resource("config.ini") # 报错,没有导入 # 我是api包下的__init__.py文件
# from policy.py
#
# from api.__init.py

test.py

绝对导入和相对导入

我们的最顶级包glance是写给别人用的,然后在glance包内部也会有彼此之间互相导入的需求,这时候就有绝对导入和相对导入两种方式:

绝对导入:以glance作为起始

相对导入:用. 或者.. 的方式做为起始(只能在一个包中使用,不能用于不同目录内)

例如:我们在 glance/api/version.py 中想要导入 glance/cmd/manage.py

#  绝对导入
import sys
# from glance.cmd import manage # 相对导入
# ValueError: attempted relative import beyond top-level package
# versions不能作为启动文件, 启动文件要与glance在同级目录
from ..cmd import manage def create_resource(conf):
manage.main()
print('from version.py: ',conf)

glance/api/version.py

测试结果:在于glance同级的 test.py 文件中测试

# 启动文件
from glance.api import versions if __name__ == '__main__':
versions.create_resource("config")

启动文件

最新文章

  1. HTLM5表单
  2. zjuoj 3607 Lazier Salesgirl
  3. http请求方法与响应状态码
  4. HDU1632+半平面交
  5. yii CListView中使用CArrayDataProvider自定义数组作为数据
  6. win10 UWP 应用设置
  7. java 中 final 的用法
  8. App上线Check List
  9. win10操作系统 安装nodejs报2503错误解决方法
  10. js 性能篇--dom 重绘 重排 节流
  11. sublime快捷键的使用
  12. 【代码笔记】Web-HTML-基础
  13. Luogu 1941 【NOIP2014】飞扬的小鸟 (动态规划)
  14. L2与L1正则化理解
  15. 学习node.js的C++扩展
  16. BZOJ1050 HAOI2006旅行(最小生成树+LCT)
  17. SpringMVC的页面几种返回方式
  18. JavaScript 里面的整数 位 操作
  19. 通过微信服务号推送Zabbix告警
  20. Delphi编程实现是否开启“平滑屏幕字体边缘“

热门文章

  1. 自由度(degree of freedom)
  2. JSON的一些要点总结 专题
  3. DataTable转换为Entity(反射&amp;&amp;泛型)
  4. Windows程序设计画图实现哆啦A梦
  5. 【必须知道】Enum_Flags
  6. java基础之super关键字
  7. Python中的参数传递问题
  8. 使用dumpbin命令查看dll导出函数及重定向输出到文件(VS自带)
  9. 插件化二(Android)
  10. 沙漏集合 good