一、面向对象
1、封装
# 广义上的封装 :把变量和函数都放在类中
# 狭义上的封装 :把一些变量 或者 方法 隐藏起来,不对外公开
# 公有的 :
# 私有的 : __名字
# 静态属性 、 对象属性、 方法(动态属性) 前面加上双下划綫都会变成私有的
# 私有的特点就是只能在类的内部调用,不能在类的外部使用
# 私有的变量 :在类的内部 如果使用__变量的形式会发生变形,python会自动的为你加上_类名
class Person:
__country = '中国' # 私有静态属性
def __init__(self,name,pwd):
self.name = name
self.__pwd = pwd # 私有的对象属性
def login(self):
print(self.__dict__)
if self.name == 'alex' and self.__pwd == 'alex3714':
print('登录成功')
alex = Person('alex','alex3714')
alex.login()
print(alex.__dict__)
# 私有的名字 只能在类的内部使用 不能在类的外部使用:AttributeError: type object 'Person' has no attribute '__country'
print(Person.__country)
# python中不并能真正的隐藏属性,是通过变形的方式实现。_Person__country
# 如果非要在类的外部调用一个私有的名字,name必须是在私有的名字前面加 _类名__私有的名字
print(Person._Person__country)
Person.__name = 'XXX'
print(Person.__name) # 在类的外部不能第一一个私有变量,只有在类定义的时候__名称才叫做私有属性
![]()
# 类内方法的调用过程
class Foo:
def __init__(self): # 第二步 找到父类的__init__ 这时候self = s 即 Son类的对象
self.func() # 第三步执行父类s.func()
def func(self):
print('in Foo')
class Son(Foo):
def func(self): # 第四步 执行func方法
print('in son')
s = Son() # 实例化类,第一步去执行__init__函数由于Son没有__init,会去父类找__init__
# 类内私有方法的调用过程
class Foo:
def __init__(self): # 第二步 找到父类的__init__ 这时候self = s 即 Son类的对象
self.__func() # 第三步 类在定义的过程中已经把私有属性变形为 self._Foo__func
# 第四步 执行self._Foo__func
def __func(self): # 类在定义的时候变形为 _Foo__func
print('in Foo')
class Son(Foo):
def __func(self): # _Son__func
print('in son')
def start(self):
self.__func()
s = Son() # 实例化类,第一步去执行__init__函数由于Son没有__init,会去父类找__init__
私有属性的调用顺序问题
![]()
import hashlib
# 隐藏加密方法
class MyLogin:
def __init__(self, name, password):
self.name = name
self.__password = password # 密码私有化
def __encryption_md5(self): # 加密方式私有化
"""
普通md5密码
:return: 加密后的字符串
"""
md5obj = hashlib.md5() # 实例化一个md5摘要算法的对象
md5obj.update(self.__password.encode('utf-8')) # 使用md5算法的对象来操作字符串
return md5obj.hexdigest()
def __encryption_md5_salt(self): # 加密方式私有化
"""
md5加盐
:return: 加密后的字符串
"""
md5obj = hashlib.md5("sunpengfei".encode("utf-8")) # 实例化一个md5摘要算法的对象
md5obj.update(self.__password.encode('utf-8')) # 使用md5算法的对象来操作字符串
return md5obj.hexdigest()
def __encryption_md5_salt1(self): # 加密方式私有化
"""
md5动态加盐
:return: 加密后的字符串
"""
md5obj = hashlib.md5(self.name.encode("utf-8")) # 实例化一个md5摘要算法的对象
md5obj.update(self.__password.encode('utf-8')) # 使用md5算法的对象来操作字符串
return md5obj.hexdigest()
def login(self):
"""
202cb962ac59075b964b07152d234b70
5dfb835be352750bb15045eae941b8c7
6b783000a3177ac09ae3706a077d6d80
:return:
"""
self.__password = self.__encryption_md5_salt1()
if self.name == "spf" and self.__password == "202cb962ac59075b964b07152d234b70":
return "Success"
if self.name == "spf" and self.__password == "5dfb835be352750bb15045eae941b8c7":
return "Success"
if self.name == "spf" and self.__password == "6b783000a3177ac09ae3706a077d6d80":
return "Success"
spf = MyLogin("spf", "1235")
print(spf.login())
练习题:登录增加mad5校验,用户不可以访问你的加密方式
2、类中的装饰器方法
![]()
# classmethod
# 如果某一个类中的方法 并没有用到这个类的实例中的具体属性,只是用到了类中的静态变量就使用类方法
class Person:
Country = '中国人'
@classmethod #把func变成了一个类方法
def func(cls): # cls是指向类的内存空间
print('当前的角色的国籍是%s'% cls.Country)
alex = Person()
alex.func()
Person.func()
classmethod
![]()
# staticmethod
# 如果 一个方法 既不会用到对象中的属性也不会用到类中的属性就应该被定义为一个静态方法
class Student:
@staticmethod
def login():
name = input('name : ')
pwd = input('pwd : ')
if name == "" and pwd == "":
print('实例化')
# Student.login()
staticmethod
![]()
# property
# 将一个函数伪装成为属性,在面向对象中动作被称为方法,但有些时候一些名词也用作方法。为了严谨可以把名词函数修改成属性
# 示例1:圆形类
# 方法 动词 —— 动作或者技能
# 名词 圆的面积 圆的周长 圆的半径
# 将一个函数伪装成为属性 @property
from math import pi
class Circle:
def __init__(self,r):
self.r = r
@property
def area(self):
return self.r ** 2 * pi
@property
def perimeter(self):
return self.r * 2 * pi
c = Circle(3)
print(c.area)
print(c.perimeter)
# 示例2:property 在__私有属性中的应用
class Goods:
"""计算商品打折后的价格"""
def __init__(self, price, discount):
self.__price = price # 价格为私有属性
self.discount = discount
@property
def price(self):
"""
计算商品打折后的价格
:return: 返回打折后的价格
"""
return self.__price * self.discount
@price.setter
def price(self, newprice):
"""
修改商品的价格
:param newprice:
:return:
"""
self.__price = newprice
@price.deleter
def price(self):
"""
删除商品的价格
:return:
"""
del self.__price
apple = Goods(8, 0.7) # 实例化一个苹果对象
print(apple.price)
apple.price = 10 # 修改苹果的价格,私有属性无法修改。所以用到了property
print(apple.price)
print(apple.__dict__)
del apple.price
print(apple.__dict__)
print(apple.price)
property
3、反射
# 什么叫反射?
# 通过字符串数据类型的 变量名 来访问变量的值
# x.y 这样的形式 都可以用反射
# 类名 反射 静态属性
class Foo:
country = "中国"
obj = Foo()
print(getattr(obj, "country"))
# 对象名 反射 对象属性 和 方法
class Foo:
country = "中国"
def __init__(self,name):
self.name = name
def func(self):
print("%s in func" %self.name)
obj = Foo("spf")
getattr(obj, "func")()
# 模块 反射 模块中的名字
import sys
from mypickle import MyPickle
choice = input(">>>:")
getattr(MyPickle, choice)(obj, "test.pkl")
# 反射 自己所在文件中的名字
from mypickle import MyPickle
choice = input(">>>:")
ret = getattr(sys.modules[__name__], choice)
ret.dump(obj, "test.pkl")
print(ret.load("test.pkl"))
# 首先 使用getattr取获取一个名字,如果在这个对象的命名空间中没有这个名字 会报错
# getattr的反射好伴侣 hasattr
# 如果使用getattr取获取一个方法,那么只能拿到这个方法的内存地址 加上括号就是执行,当然,括号里的参数可以照传不误
# 如果getattr获取一个属性,那么直接使用反射就可以获取到值
4、类的内置方法
![]()
# __new__ 构造方法 创建一个对象
# __init__ 初始化方法
class Foo:
def __new__(cls, *args, **kwargs):
print('执行我啦')
obj = object.__new__(cls)
print(obj)
return obj
def __init__(self):
print('1', self)
Foo()
# 先执行new方法,object.new()
# 再执行init
# Foo() --> python解释器接收到你的python代码
# python解释器替你去做了很多操作
# 包括 主动帮助你 调用 new方法 去创造一个对象 —— 开辟内存空间 —— python语言封装了开辟内存的工作
# object的new方法里 —— 帮你创造了对象
# 调用init用到的self参数 就是new帮你创造的对象
__new__
![]()
# 什么叫单例模式
# 单例模式 : 某一类 只有一个实例
class Person:
__isinstance = None
def __new__(cls, *args, **kwargs):
if not cls.__isinstance :
obj = object.__new__(cls)
cls.__isinstance = obj
return cls.__isinstance
def __init__(self,name):
self.name = name
alex = Person('alex')
alex.age = 18
egon = Person('egon')
print(egon.age)
print(id(alex))
print(id(egon))
print(alex.name)
print(egon.name)
# __new__生孩子
# 类 : 生一个小孩__new__ 给这个小孩穿衣服 __init__
# 单例模式下的类 : 只有一个小孩
__new__单例模式
二、模块
1、序列化
什么叫序列化呢?
数据类型 —> 字符串的过程
什么时候要用序列化呢?
数据从内存到文件
数据在网络上传输 字节 - 字符串 - 字典
python中的序列化模块都有哪些?
json 通用的 支持的数据类型 list tuple dict
pickle python中通用的 支持几乎所有python中的数据类型
shelve python中使用的便捷的序列化工具
![]()
# json
# dumps loads
# 内存读写
import json
dic = {"k": 'v'}
json_dic = json.dumps(dic) # 字典转字符串的过程 ——序列化
print(json_dic)
print(json.loads(json_dic)) # 字符串转回其他数据类型 —— 反序列化
# dump load
# 文件读写
dic = {"k": 'v'}
with open('test.json', 'w') as f:
json.dump(dic, f)
# json.dump(dic, f) # 在json中 dump默认不支持dump多条数据
with open('test.json') as f:
print(json.load(f)) # 从文件中反序列化
# 如果要dump多条数据,每一条数据线dumps一下 编程字符串 然后打开文件 write写进文件里 \n
# 读取的时候按照标志读取或者按行读,读出来之后 再使用loads
with open('test.json', 'w') as f:
str_dic = json.dumps(dic)
f.write(str_dic+'\n')
f.write(str_dic+'\n')
f.write(str_dic+'\n')
f.write(str_dic+'\n')
with open('test.json') as f:
for line in f:
print(json.loads(line.strip()))
json
![]()
# pickle 和 json用法一致。但是pickle可以dump多条数据
# 1.pickle支持更多的数据类型
# 2.pickle的结果是二进制
# 3.pickle在和文件交互的时候可以被多次load
import pickle
class A:
def __init__(self,name):
self.name = name
alex = A('alex')
print(pickle.dumps(alex))
with open('test.pkl', 'wb') as f:
pickle.dump(alex, f)
pickle.dump(alex, f) # 可以dump多条数据
pickle.dump(alex, f) # 可以dump多条数据
with open('test.pkl', 'rb') as f:
while True: # 通过循环取出所有的数据
try:
obj = pickle.load(f)
print(obj.name)
except EOFError:
break
pickle
![]()
# shelve也是python提供给我们的序列化工具,比pickle用起来更简单一些。
# shelve只提供给我们一个open方法,是用key来访问的,使用起来和字典类似。
import shelve
f = shelve.open('shelve_file')
f['key'] = {'int':10, 'float':9.5, 'string':'Sample data'} #直接对文件句柄操作,就可以存入数据
f.close()
import shelve
f1 = shelve.open('shelve_file')
existing = f1['key'] #取出数据的时候也只需要直接用key获取即可,但是如果key不存在会报错
f1.close()
print(existing)
shelve
![]()
import pickle
class MyPickle:
@staticmethod
def load_iter(filename):
"""
反序列化
:param filename: 反序列化的文件路径
:return: 一个存放反序列化数据的生成器
"""
with open(filename, "rb") as f:
while True:
try:
yield pickle.load(f)
except EOFError:
break
@staticmethod
def load(filename):
"""
反序列化
:param filename: 反序列化的文件路径
:return: 一个列表,列表存储一个或者多个反序列化后的数据
"""
result = []
with open(filename, "rb") as f:
while True:
try:
result.append(pickle.load(f))
except EOFError:
break
return result
@staticmethod
def dump(obj, filename):
"""
序列化
:param obj: 需要进行序列化的对象
:param filename: 序列化后文件存储路径
:return:
"""
with open(filename, "ab") as f:
pickle.dump(obj, f)
练习:MyPickle
2、hashlib
import hashlib
# hashlib特性:
# 1、单向不可逆 (采用摘要算法)
# 2、将一个字符串进行摘要运算 拿到不变的 固定长度的值
# 用途:
# 1、存储用户密码的时候 : 不要存储明文
# # 注册 :alex3714 -> 摘要-> 文件里
# # 登录 :alex3714 -> 摘要-> 和文件里比对
# md5加密
# 普通版本
md5obj = hashlib.md5() # 实例化一个md5摘要算法的对象
md5obj.update("123".encode("utf-8")) # 使用md5算法的对象来操作字符串
print(md5obj.hexdigest()) # 获取算法的结果
# # 升级版本加盐
md5obj = hashlib.md5("sunpengfei".encode("utf-8"))
md5obj.update("123".encode("utf-8"))
print(md5obj.hexdigest())
# 动态加盐
username = "sunpengfei" # 动态加盐可以把用户名当做盐
md5obj = hashlib.md5(username.encode("utf-8"))
md5obj.update("123".encode("utf-8"))
print(md5obj.hexdigest())
# sha1加密
# sha1 用法和md5一模一样
sha1obj = hashlib.sha1()
sha1obj.update("123".encode("utf-8"))
print(sha1obj.hexdigest())
![]()
# 校验文件一致性,当文件很大的时候,可以对文件进行一部分一部分验证。最后得到的结果还是一样的
md5obj = hashlib.md5() # 实例化一个md5摘要算法的对象
md5obj.update('alex'.encode('utf-8')) # 使用md5算法的对象来操作字符串
md5obj.update('3714'.encode('utf-8')) # 使用md5算法的对象来操作字符串
print(md5obj.hexdigest())
md5obj = hashlib.md5() # 实例化一个md5摘要算法的对象
md5obj.update('alex3714'.encode('utf-8')) # 使用md5算法的对象来操作字符串
print(md5obj.hexdigest())
# 练习题:对比两个文件
def check_file(src_file, dest_file):
"""
对比两个文件是否一致
:param src_file: 源文件
:param dest_file: 目标文件
:return: True/False
"""
ret = []
for file in [src_file, dest_file]:
md5obj = hashlib.md5()
with open(file, encoding="utf-8") as f1:
for i in f1:
md5obj.update(i.encode('utf-8'))
ret.append(md5obj.hexdigest())
return True if ret[0] == ret[1] else False
print(check_file("./test.py", "test.py"))
练习题:对比文件的md5值
3、logging
#!/usr/bin/env python3
# 函数式简单配置
import logging
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')
# 默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志,
# 这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG),默认的日志格式为日志级别:Logger名称:用户输出消息。
# 灵活配置日志级别,日志格式,输出位置:
import logging
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
datefmt='%a, %d %b %Y %H:%M:%S',
filename='./test.log',
filemode='w')
logging.debug('debug message') # 非常细节的日志 —— 排查错误的时候使用
logging.info('info message') # 正常的日志信息
logging.warning('warning message') # 警告
logging.error('error message') # 错误
logging.critical('critical message') # 严重错误
# 参数
"""
logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:
filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
format:指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
level:设置rootlogger(后边会讲解具体概念)的日志级别
stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。
format参数中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s用户输出的消息
"""
# logger对象配置
import logging
logger = logging.getLogger() # 实例化一个logger对象
fh = logging.FileHandler('test1.log', encoding='utf-8') # 创建一个文件handler,用于写入日志文件
ch = logging.StreamHandler() # 再创建一个终端handler,用于输出到控制台
formatter_File = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 定义文件日志格式
formatter_Stream = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 定义终端日志格式
logger.setLevel(logging.DEBUG) # 定义文件日志级别
logger.setLevel(logging.DEBUG) # 定义终端日志级别
fh.setFormatter(formatter_File) # 设置文件日志格式
ch.setFormatter(formatter_Stream) # 设置终端日志格式
logger.addHandler(fh) # logger对象可以添加多个fh和ch对象
logger.addHandler(ch)
logger.debug('logger debug message')
logger.info('logger info message')
logger.warning('logger warning message')
logger.error('logger error message')
logger.critical('logger critical message')
# logging库提供了多个组件:Logger、Handler、Filter、Formatter。Logger对象提供应用程序可直接使用的接口,
# Handler发送日志到适当的目的地,Filter提供了过滤日志信息的方法,Formatter指定日志显示格式。另外,可以通过:logger.setLevel(logging.Debug)设置级别,当然,也可以通过
#
# fh.setLevel(logging.Debug)单对文件流设置某个级别。
def my_logger(log_file):
"""
定义日志输出合格
:return: 返回一个可以直接使用的logger对象
"""
import logging
logger = logging.getLogger() # 实例化一个logger对象
fh = logging.FileHandler(log_file, encoding='utf-8') # 创建一个文件handler,用于写入日志文件
ch = logging.StreamHandler() # 创建一个终端handler,用于输出到控制台
formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s %(message)s File:<%(filename)s line %(lineno)d>') # 定义文件日志格式
logger.setLevel(logging.DEBUG) # 定义文件日志级别
fh.setFormatter(formatter) # 设置文件日志格式
ch.setFormatter(formatter) # 设置终端日志格式
logger.addHandler(fh) # logger对象fh对象
logger.addHandler(ch) # logger对象ch对象
return logger
log = my_logger("./test1.log")
log.debug('logger debug message')
log.info('logger info message')
log.warning('logger warning message')
log.error('logger error message')
log.critical('logger critical message')
4、configparser
#!/usr/bin/env python3
# 该模块适用于配置文件的格式与windows ini文件类似,可以包含一个或多个节(section),每个节可以有多个参数(键=值)。
"""
[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
[bitbucket.org]
User = hg
[topsecret.server.com]
Port = 50022
ForwardX11 = no
"""
# 如果想用python生成一个这样的文档怎么做呢?
import configparser
config = configparser.ConfigParser()
config["DEFAULT"] = {'ServerAliveInterval': '45',
'Compression': 'yes',
'CompressionLevel': '9',
'ForwardX11':'yes'
}
config['bitbucket.org'] = {'User':'hg'}
config['topsecret.server.com'] = {'Host Port':'50022','ForwardX11':'no'}
with open('example.ini', 'w') as configfile:
config.write(configfile)
# 查找文件
import configparser
config = configparser.ConfigParser()
#查找文件内容,基于字典的形式
print(config.sections()) # []
config.read('example.ini')
print(config.sections()) # ['bitbucket.org', 'topsecret.server.com']
print('bytebong.com' in config) # False
print('bitbucket.org' in config) # True
print(config['bitbucket.org']["user"]) # hg
print(config['DEFAULT']['Compression']) #yes
print(config['topsecret.server.com']['ForwardX11']) #no
print(config['bitbucket.org']) #<Section: bitbucket.org>
for key in config['bitbucket.org']: # 注意,有default会默认default的键
print(key)
print(config.options('bitbucket.org')) # 同for循环,找到'bitbucket.org'下所有键
print(config.items('bitbucket.org')) #找到'bitbucket.org'下所有键值对
print(config.get('bitbucket.org','compression')) # yes get方法Section下的key对应的value
# 增删改操作
import configparser
config = configparser.ConfigParser()
config.read('example.ini')
config.add_section('yuan')
config.remove_section('bitbucket.org')
config.remove_option('topsecret.server.com',"forwardx11")
config.set('topsecret.server.com','k1','11111')
config.set('yuan','k2','22222')
config.write(open('new2.ini', "w"))