Python开发【第七篇】: 面向对象和模块补充
内容概要
- 特殊成员
- 反射
- configparser模块
- hashlib模块
- logging模块
- 异常处理
- 模块
- 包
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")
启动文件
Python开发【第七篇】: 面向对象和模块补充的更多相关文章
- Python开发【第*篇】【模块】
模块分为三种: 自定义模块 第三方模块 内置模块 1.模块导入 import model from model.xx.xx import xx from model.xx.xx import xx a ...
- Python开发【内置模块篇】os模块
1.当前路径及路径下的文件 os.getcwd():查看当前所在路径. >>> import os >>> os.getcwd() 'E:\\test' >& ...
- Python开发【内置模块篇】日志模块
logging配置 import logging logging.basicConfig(level=logging.WARNING, format='%(asctime)s %(filename)s ...
- Python开发【第一篇】:目录
本系列博文包含 Python基础.前端开发.Web框架.缓存以及队列等,希望可以给正在学习编程的童鞋提供一点帮助!!! Python开发[第一篇]:目录 Python开发[第二篇]:初识Python ...
- Python开发【第二篇】:初识Python
Python开发[第二篇]:初识Python Python简介 Python前世今生 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏 ...
- Python开发【第一篇】:目录
本系列博文包含Python基础.前端开发.Web框架.缓存以及队列等,希望可以给正在学习Python编程的朋友们提供一点帮助! .Python开发[第一篇]:目录 .Python开发[第二篇]:初始P ...
- Python开发【第一篇】Python基础之自定义模块和内置模块
为什么要有模块,将代码归类.模块,用一砣代码实现了某个功能的代码集合. Python中叫模块,其他语言叫类库. 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代 ...
- 七丶人生苦短,我用python【第七篇】
模块 模块,用一砣代码实现了某个功能的代码集合. 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一个复杂的功能来,可能需要多个 ...
- python【第五篇】常用模块学习
一.主要内容 模块介绍 time &datetime模块 random os sys shutil json & pickle shelve xml处理 yaml处理 configpa ...
随机推荐
- hann function
hann function 是一种离散型窗函数,定义如下: w(n)=12(1−cos(2πnN−1))=sin2(πnN−1) 窗口的长度为 L=N+1; hann function 以及其傅里叶响 ...
- 作为学术用的 matlab
1. 可重复实验 程序实现的所谓随机,并非完全的随机,而是由某一算法(或者再需要一个种子值)生成出来的. randn('state', 0) % set state so that example c ...
- HDU 3172 Virtual Friends 并与正确集中检查 -秩
ll T; while(~scanf("%d",&T)){ while(T--) { = = ... 思路: 用秩合并,看了题解才发现 if(fx == fy)要输出当前集 ...
- hdu 4035 可能性DP 成都网络游戏
http://acm.hdu.edu.cn/showproblem.php?pid=4035 获得: 1.首先推断是不是树.事实上,所有的感觉身影,既看边数==算-1是不成立 2.有时候,我告诉孩子来 ...
- 解压压缩文件报错gzip: stdin: not in gzip format tar: Child returned status 1 tar: Error is not recoverable: exiting now
压缩包是直接weget 后面加官网上的tar包地址获取的 [root@xuegod43 ~]# tar -zxvf /home/hadoop/hadoop-2.6.5-src.tar.gz gzip ...
- DataTemplate
DataTemplate作用是布局+数据绑定 使用DataTemplate 同时完成样式布局和数据绑定 <Window.Resources> <DataTemplate x:Key= ...
- 获取同时间段不同的时间 php
/** * 根据指定日期返回经过的年月 * @param string $sDay 开始日期 * @param string $eDay 结束日期 * @returnse multitype:stri ...
- Windows实用小工具-问题步骤记录器
今晚给大家介绍个实用的好工具,可以做简单的问题记录,再也不用截图加注释这么辛苦了····· 经测试,这东东在win7,2008 及2008R2里适用,也就是说,在win7以上的系统中才有.好了,下面直 ...
- 记一次ASP.NET MVC4 升级到MVC5的小问题解决
原文:记一次ASP.NET MVC4 升级到MVC5的小问题解决 .NET 4.0 MVC4版本,升级到.NET 4.6.1 MVC5: 1.使用nuget更新所有 与mvc相关的类库; 2.更改~/ ...
- fastjson 出现首字母小写的问题
今天工作使用fastjson要求传过去的参数全为大写,在使用的过程中发现它自动将我的字段首字母转为小写了,在网上查了一些资料,发现下面的这个挺好,比其他的要方便. package com.alibab ...