反射

#!/usr/bin/env python3
# author: Alnk(李成果)
# 反射
# hasattr getattr setattr delattr class Animal(object):
gender = 'male' def __init__(self, name, age):
self.name = name
self.age = age def foo(self):
print('foo...') # hasattr: 判断一个对象中有没有某种方法或属性
tom = Animal('tom', 34)
ret = hasattr(tom, 'name') # 判断tom这个对象是否有name这个属性或者方法,等价与tom.name 如果有返回True,否则False
print(ret) # True
ret2 = hasattr(tom, 'gender')
print(ret2) # True
ret3 = hasattr(tom, 'run')
print(ret3) # False
print("---------------- 1 ------------------------------") # getattr: 获取对象中的某种属性或方法
tom = Animal('tom', 34)
ret = getattr(tom, 'name') # 如果name存在并且为属性,则返回属性对应的值,等价于tom.name
print(ret) # tom ret2 = getattr(tom, 'foo') # 如果foo存在并且为方法,则返回内存地址,等价与tom.foo
print(ret2) # <bound method Animal.foo of <__main__.Animal object at 0x7fdd2c2f2e10>>
ret2() # foo... tom.foo() ret3 = getattr(tom, 'run', None) # 如果run不存在则返回None,不设置None的话,直接报错
print(ret3)
# ret4 = getattr(tom, 'run') # AttributeError: 'Animal' object has no attribute 'run'
print("---------------- 2 ------------------------------") # hasattr 和 getattr 组合使用(常用的)
tom = Animal('tom', 34)
if hasattr(tom, 'name'):
ret = getattr(tom, 'name')
print(ret) # tom
else:
print('没有该方法或者属性') if hasattr(tom, "foo"):
f = getattr(tom, "foo")
f() # foo...
else:
print("没有该方法或者属性")
print("---------------- 3 ------------------------------") # setattr: 增加或者修改一个属性或者方法
# 增加一个属性
tom = Animal('tom', 34)
print(tom.__dict__) # {'name': 'tom', 'age': 34}
setattr(tom, 'job', 'IT') # 在tom的实例对象空间新增加一个属性
print(tom.__dict__) # {'name': 'tom', 'age': 34, 'job': 'IT'} print(getattr(tom, 'job')) # IT
print(tom.job) # IT
print("---------------- 4 ------------------------------") # 修改一个属性
print(tom.age) # 34
setattr(tom, 'age', 1000)
print(tom.age) # 1000
print("---------------- 5 ------------------------------") # 增加一个方法
def bar():
print('bar...') setattr(tom, 'bar', bar)
tom.bar()
print(tom.__dict__) # {'name': 'tom', 'age': 1000, 'job': 'IT', 'bar': <function bar at 0x7fd654dc6ea0>}
print("---------------- 6 ------------------------------") # delattr:删除一个属性
tom = Animal('tom', 34)
print(tom.__dict__) # {'name': 'tom', 'age': 34}
delattr(tom, 'age')
print(tom.__dict__) # {'name': 'tom'}
# 报错
# print(tom.age) # AttributeError: 'Animal' object has no attribute 'age'
print("---------------- 7 ------------------------------") # 基于字典的映射分发方式
# def check():
# print('check...')
# def pay():
# print('pay...')
# def withdraw():
# print('withdraw...')
# data={
# "check": check,
# "pay": pay,
# "withdraw": withdraw,
# }
# while 1:
# for i in data.keys():
# print(i)
# action = input('>>>:')
# data[action]() # 反射的应用
# 基于反射的分发
class Atm(object):
option_lis = [
('check', '检查'),
('pay', '支付'),
('withdraw', '提现'),
('login_out', '退出'),
] def check(self):
print('check...') def pay(self):
print('pay...') def withdraw(self):
print('withdraw...') def login_out(self):
exit() def view(self):
while 1:
for k, i in enumerate(self.option_lis, 1):
print(k, i[1])
action = int(input('请输入编号>>:'))
if 0 <= (action - 1) < len(self.option_lis):
if hasattr(self, self.option_lis[action - 1][0]):
getattr(self, self.option_lis[action - 1][0])()
else:
print('不存在该方法或者属性')
else:
print('编号有误') a = Atm()
a.view()

类的魔法方法

__new__构造方法单例模式

#!/usr/bin/env python3
# author: Alnk(李成果) # __方法 : 私有方法
# __方法__: 魔法方法 class Animal(object):
def __new__(cls, *args, **kwargs): # 构造方法
print('这是一个构造方法')
addr = super().__new__(cls) # 开辟一块内存空间
print('addr', addr)
return addr # 一定要return,addr会传到 __init__ 方法中的self def __init__(self, name, age): # 初始化方法
print('这是一个初始化方法')
print('self', self) # 这个self就是 __new__ 中的addr
self.name = name
self.age = age tom = Animal('tom', 18)
# 实例化的时候,输出结果如下
# 这是一个构造方法
# addr <__main__.Animal object at 0x7fb2872e7c18>
# 这是一个初始化方法
# self <__main__.Animal object at 0x7fb2872e7c18> # tom = Animal('tom', 18)
# 1. __new__ 方法获得内存空间地址
# 2. 将空间地址作为第一个参数传给 __init__ ,完成实例空间赋值属性的过程
print("----------------- 1 ------------------------------") # 应用场景
# 一般类
class Dog(object):
name = "小明" # 实例化两次,产生不同的内存地址,等于开辟了两块内存空间
t = Dog()
jerry = Dog()
print('tom', id(t)) # 140290412195008
print('jerry', id(jerry)) # 140290412194952
t.name = "小红"
print(t.name, jerry.name) # 小红 小明
print("----------------- 2 ------------------------------") # 单例模式: 一个类只能实例化一个对象 例如配置文件类,只需加载一次,节省资源
class Config(object):
"""配置文件类"""
file_path = ''
data = 'mysql'
app = 'LOL'
_instance = None # 标记位 def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls) # 会开辟一块内存空间
# print("_instance", cls._instance) # _instance <__main__.Config object at 0x7fc1762e7f98>
return cls._instance # 实例化两次,产生相同的内存地址,等同于只开辟了一块内存空间
cf1 = Config()
cf2 = Config()
print('cf1', id(cf1)) # 140669493346088
print('cf2', id(cf2)) # 140669493346088
cf1.app = "CF"
print(cf1.app, cf2.app) # CF CF # 第一次实例化时,由于此时标记位 _instance = None,所以程序会走if判断,会开辟一块内存空间并且赋值给 _instance
# 第二次以及以后的实例化时,此时的标记位 _instance 已经不为None,所以会直接返回第一次开辟的内存空间

__init__初始化方法

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
# @Author: Alnk(李成果)
# @Email: 1029612787@qq.com
# @Date: 2021/4/22 3:04 下午
""" # __init__ 初始化方法 class Animal(object): def __init__(self):
"""初始化方法"""
print("我在类的实例化的时候自动执行")
print("创建数据库连接...")
print("程序开始启动...") a = Animal()
# 打印结果
# 我在类的实例化的时候自动执行
# 创建数据库连接...
# 程序开始启动...

__str__格式化打印类的实例对象

#!/usr/bin/env python3
# author: Alnk(李成果)
# 打印类的任何 实例 对象,都返回 __str__ 的返回值
# 注意: 返回值必须是字符串 class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age def __str__(self):
"""打印类的任何实例对象,都返回 __str__ 的返回值"""
# return self.name # 返回值必须是字符串
return "姓名:%s,年龄:%s" % (self.name, str(self.age)) t = Animal('tom', 18)
j = Animal('jerry', 28)
print(t) # 姓名:tom,年龄:18
print(j) # 姓名:jerry,年龄:28
print(t.name) # tom # 当注释掉自定义的 __str__ 方法以后,会有如下返回
# print(t) # <__main__.Animal object at 0x7f96fe9c5278>
# print(j) # <__main__.Animal object at 0x7f96fe9c5358>

__call__可调用对象

#!/usr/bin/env python3
# author: Alnk(李成果) # __call__
# 只要可以调用的类,内部一定有 __call__ 方法,如果没有则不能调用 # Python中万物都可称为对象,但对象与对象之间也有差别
# 对象有 可被调用对象 和 不可被调用对象 之分
# Python 中,凡是可以将 () 直接应用到自身并执行,都称为可调用对象
# 当一个对象为可被调用对象时,callable(object)返回为True,否则为False:
# 实例是不可以被调用的,但是通过 __call__ 方法可以让实例可以调用 class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age def foo(self):
print('foo...') # callable: 判断一个对象是否可以调用
tom = Animal("tom", 12)
print(callable(tom.foo)) # True tom.foo() 可以调用
print(callable(Animal.foo)) # True Animal.foo() 可以调用
print(callable(tom)) # False tom() 不能调用
# tom() # 报错 # 在上面的类中增加 __call__ 方法
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age def foo(self):
print('foo...') def __call__(self, *args, **kwargs):
"""增加__call__方法,保证了类的实例可以被调用"""
print('i am call...') t = Animal("tom", 18)
print(callable(t)) # True 可以调用了
t() # i am call...

__del__析构方法程序首尾操作

#!/usr/bin/env python3
# author:Alnk(李成果) # 析构方法:__del__
# 在程序结束,或者类在内存中被删除的时候,就会执行 __del__ 方法
# python 垃圾回收机制:任何对象在执行过程中失去了指引,则被回收 import time class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age def foo(self):
print('foo...') def __del__(self):
print('我是析构方法...')
print("断开与数据库连接...")
print("保存文件...") # 会等待5s,程序结束,然后执行 __del__ 析构方法
# tom = Animal('tom', 34)
# time.sleep(5)
"""
# 输出结果
# 我是析构方法...
# 断开与数据库连接...
# 保存文件...
""" # 会先执行 __del__ 析构方法,然后等待5s 程序结束
tom = Animal('tom', 34)
del tom
time.sleep(5) # 应用场景
# 例如:关闭数据库连接,关闭文件连接等

__getitem__ __setitem__ __delitem__

#!/usr/bin/env python3
# author: Alnk(李成果) class Animal(object):
gender = 'male' def __init__(self, name, age):
self.name = name
self.age = age def __getitem__(self, item):
print('getitem被调用')
print('大量的业务逻辑代码...')
# 下面第二个return的写法更好,避免程序报错
# return self.__dict__[item] # 如果找不到item变量,程序报错
return getattr(self, item, None) # 如果找不到item变量,则返回None def __setitem__(self, key, value):
print('__setitem__ 被调用')
self.__dict__[key] = value def __delitem__(self, key):
print('__delitem__被调用')
del self.__dict__[key] # __getitem__(self, item) 返回键对应的值
# 调用一个属性前,加自己的业务逻辑等
# 调用属性 必须要使用 tom["name"] 这种方法调用才会触发 __getitem__ 方法
tom = Animal('tom', 18)
print(tom['name'])
print("------------- 1 --------------------")
# 输出结果如下
# getitem被调用
# 大量的业务逻辑代码...
# tom print(tom.name) # 没有触发 __getitem__
print("------------- 2 --------------------") print(tom['age']) # tom.age
print("------------- 3 --------------------") print(tom['gender']) # tom.gender
print("------------- 4 --------------------") print(tom['job']) # tom.job
print("------------- 5 --------------------") # __setitem__(self, key, value): 设置给定键的值
jerry = Animal('jerry', 19)
jerry['job'] = 'IT' # __setitem__ 被调用
jerry['gender'] = 'F' # __setitem__ 被调用
print("------------- 6 --------------------") print(jerry['job'])
print(jerry.__dict__) # {'name': 'jerry', 'age': 19, 'job': 'IT', 'gender': 'F'}
print("------------- 7 --------------------") # __delitem__:删除给定键对应的元素
tom = Animal('tom', 19)
print(tom.__dict__) # {'name': 'tom', 'age': 19} del tom['name'] # __delitem__被调用 print(tom['name'])
# getitem被调用
# 大量的业务逻辑代码...
# None print(tom.__dict__) # {'age': 34}

__getattr__ __setattr__

#!/usr/bin/env python3
# author: Alnk(李成果) # getattr
class Animal(object):
def __init__(self, name, age):
self.name = name
self.age = age def __getattr__(self, item):
print('getattr', item)
return '对象没有%s属性' % item # __getattr__
# 如果属性查找在实例以及对应的类中(通过__dict__)失败
# 那么会调用到类的__getattr__函数
# 如果没有定义这个函数,那么抛出 AttributeError 异常
# 由此可见 __getattr__ 是作用于属性查找的最后一步,兜底
# 当使用点号获取实例属性时,如果属性不存在就自动调用__getattr__方法
tom = Animal('tom', 55)
print(tom.age) # 55 # 实例中没有gender属性,所以调用__getattr__方法
print(tom.gender)
print("------------ 1 ------------------------")
# 输出结果
# getattr gender
# 对象没有gender属性 # setattr
class Animal(object):
def __init__(self, name, age):
self.name = name # object类一定会有__setattr__方法
self.age = age # 注意: 初始化的时候会调用 __setattr__(self, key, value) 进行赋值 def __setattr__(self, key, value):
# 打印日志
print("空间地址%s为%s属性赋值%s" % (self, key, value))
super().__setattr__(key, value) # 继承父类的__setattr__ 方法 # __setattr__
# 当类实例化时,会调用 __init__ 方法,__init__ 方法设置属性的时候会调用 __setattr__
tom = Animal('tom', 55)
# 输出结果
# 空间地址<__main__.Animal object at 0x7f9e08af2cc0>为name属性赋值tom
# 空间地址<__main__.Animal object at 0x7f9e08af2cc0>为age属性赋值55 tom.name = 'lisa' # 空间地址<__main__.Animal object at 0x7f9a362e7c88>为name属性赋值lisa
print(tom.name) # lisa # tom = Animal('tom', 55)
# 类实例化的过程:
# 1. __new__: 开辟内存空间,传递空间地址给 __init__
# 2.__init__: 接收 __new__ 传递过来的空间地址,并且进行赋值操作
# 3.__setattr__: 接收 __init__ 的赋值,在内存中进行赋值

__eq__

#!/usr/bin/env python3
# author:Alnk(李成果) class Student(object):
def __init__(self, name, age):
self.name = name
self.age = age def __eq__(self, other):
if self.name == other.name and self.age == other.age:
return True tom = Student('tom', 34)
jerry = Student('tom', 34) # __eq__ 方法注释就为False,否则为True
print(tom == jerry)

__len__

#!/usr/bin/env python3
# author:Alnk(李成果) class Data(object): def __len__(self):
return 345 # 只能返回数字 d = Data()
print(len(d)) # 345
print(d.__len__()) # 345

__dict__

#!/usr/bin/env python3
# author:Alnk(李成果) class Dog(object):
x = 100 @classmethod
def speak(cls):
print("来 请开始你的表演") def __init__(self, name):
self.name = name def talk(self):
print('%s is wang wang wang ' % self.name) # 通过类调用,以字典形式打印类中的所有方法和属性,不包括实例属性
print(Dog.__dict__)
"""
{
'x': 100,
'speak': <classmethod object at 0x7f8ed2aba278>,
'__init__': <function Dog.__init__ at 0x7f8ed2ae19d8>,
'talk': <function Dog.talk at 0x7f8ed2ae18c8>,
}
""" # 只打印实例属性,不包括类属性
d = Dog('tom')
print(d.__dict__) # {'name': 'tom'}

练习题1

#!/usr/bin/env python3
# author:Alnk(李成果)
# 1、new方法和init方法执行的执行顺序
# 先执行 __new__ 方法,后执行 __init__ 方法 # 2、call方法在什么时候被调用
# # 3、请用写一个类,用反射为这个类添加一个静态属性
class A(object):
pass def foo():
print("in foo...") setattr(A, "foo", foo)
A.foo() # in foo...
print("-------------- 1 ----------------------------") # 4、请用反射为上题的类的对象添加一个属性name,值为你的名字
setattr(A, "name", "Alnk")
print(A.name) # Alnk
print("-------------- 2 ----------------------------") # 5、请使用new方法实现一个单例模式
class Config(object):
"""配置文件类"""
user = "root"
pwd = "root123"
db_name = "mysql"
ip = "192.168.1.2"
port = 3306
_instance = None def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super().__new__(cls)
return cls._instance cf1 = Config()
print(id(cf1)) # 140441860078896
cf2 = Config()
print(id(cf2)) # 140441860078896
cf1.user = "admin"
print(cf1.user, cf2.user) # admin admin
print("-------------- 3 ----------------------------") # 6、校验两个文件的一致性
import hashlib def get_md5(path: str):
"""
获取文件的md5
@param path: 文件路径
@return: 文件md5串
"""
md5 = hashlib.md5() with open(path) as f:
for i in f.readline():
print("+++", i)
md5.update(i.encode("utf-8"))
return md5.hexdigest() a = get_md5("a.txt")
b = get_md5("b.txt")
print(a == b)
print("-------------- 4 ----------------------------") # 7、加密的密文登陆
import getpass def login():
user_name = input("用户名>>>:")
user_pwd = getpass.getpass("密码>>>:") # 在终端好使,在pycharm不可以 if user_name == "alnk" and user_pwd == "123":
print("登录成功")
else:
print("登录失败") # login()
print("-------------- 5 ----------------------------") # 8、完成一个既可以向文件输出又可以向屏幕输出的日志设置
import logging.config # 定义三种日志输出格式 开始
# 标准版
standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \
'[%(levelname)s][%(message)s]' # 其中name为getlogger指定的名字 # 定义日志输出格式 结束
logfile_name = r'staff.log' # log文件名 # log配置字典
LOGGING_DIC = {
'version': 1, # 版本
'disable_existing_loggers': False, # 可否重复使用之前的logger对象
'formatters': {
'standard': {
'format': standard_format
},
},
'filters': {},
'handlers': {
# 打印到终端的日志
'stream': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # 打印到屏幕
'formatter': 'standard'
},
# 打印到文件的日志,收集info及以上的日志 文件句柄
'file': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件
'formatter': 'standard', # 标准
'filename': logfile_name, # 日志文件
'maxBytes': 30000, # 日志大小 30000 bit
'backupCount': 5, # 轮转文件数
'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了
},
},
'loggers': {
# logging.getLogger(__name__)拿到的logger配置
'': {
'handlers': ['stream', 'file'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
'level': 'DEBUG', # 总级别
'propagate': True, # 向上(更高level的logger)传递
},
},
} def load_my_logging_cfg():
logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging配置
logger = logging.getLogger(__name__) # 生成一个log实例
logger.info('It works!') # 记录该文件的运行状态 load_my_logging_cfg()

练习2-ATM项目

README.txt

作者: Alnk(李成果)
版本:v1.0 程序介绍
功能全部用python知识完成,用到了 os\sys\json\模块导入\面向对象编程 等知识
用户角色:
实现ATM常用功能,如查看账户信息、提现、还款、转账、支付等
管理员角色:
添加账户、调整用户额度、冻结/激活账户、查看用户信息等功能 程序启动方式
直接运行 atm/bin/start.py 文件 登录账号密码
管理员账号
账号:admin
密码:123
普通用户
账号:123456
密码:123 程序结构
├── atm
├─bin # 执行目录
│ │ start.py # 执行文件
| | __init__.py # 包文件

├─conf # 配置文件目录
│ │ settings.py # 配置文件
| | __init__.py # 包文件

├─core # 核心代码目录
│ │ admin.py # 管理员模块
│ │ basic.py # 基础模块
│ │ decorator.py # 装饰器模块,外部调用付款接口时会用到
│ │ log.py # 日志模块
│ │ login.py # 登录认证木块
│ │ main.py # 功能分发模块
│ │ user.py # 普通用户模块
│ │ payment.py # 第三方调用支付模块
| | __init__.py # 包文件

├─db # 数据目录
│ └─user_info # 用户登录信息目录
│ 123456.json # 普通文件,可以用管理员账号创建
│ admin.json # 管理员初始化文件,记录账号密码权限等信息
| __init__.py # 包文件

├─docs # 文档目录
│ README.txt # 程序说明文件
│ 需求.txt # 需求

├─log # 日志记录目录
│ 123456.log # 普通用户日志
│ admin.log # 管理员日志文件 db/user_info/admin.json 文件示例说明
user_dict = {
'name': name, # 账号
'pwd': pwd, # 密码
'role': role, # 角色 管理员:admin, 用户:user
'total_quota': total_quota, # 总额度
'available_quota': total_quota, # 可用额度
'state': 1, # 状态 1:可用,0:冻结
}

start.py

#!/usr/bin/env python3
# author: Alnk(李成果)
import os
import sys # 添加项目目录到python环境
base_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # 项目目录
# print(base_path)
sys.path.insert(0, base_path)
# print(sys.path) from atm.core.main import view # 程序入口,就是这么简单
if __name__ == '__main__':
view()

settings.py

#!/usr/bin/env python3
# author:Alnk(李成果)
import os # 程序目录
base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 用户数据库目录与文件
# 数据库目录
db_path = os.path.join(base_path, 'db')
# 用户信息目录
user_info_path = os.path.join(db_path, 'user_info') # 日志目录
log_path = os.path.join(base_path, 'log')
# admin管理员日志文件
admin_log_path = os.path.join(log_path, 'admin.log') # 提现手续费
service_charge = 0.05

admin.py

#!/usr/bin/env python3
# author:Alnk(李成果)
import os
from atm.core.basic import Basic
from atm.conf import settings
from atm.core.log import get_logger class Admin(Basic):
"""管理类""" option_lis = [
('添加账户', 'create_user'),
('调整用户额度', 'modify_quota'),
('冻结/激活账户', 'frozen_user'),
('查看用户信息', 'check_user_info'),
('退出', 'login_out'),
] def __init__(self, name):
self.name = name def create_user(self):
"""
创建用户
:return:
"""
name = input('\n输入卡号>>>:')
pwd = input('输入密码>>>:')
total_quota = 15000 # 额度
role = 'user' # 角色
user_dict = {
'name': name, # 账号
'pwd': pwd, # 密码
'role': role, # 角色 管理员:admin, 用户:user
'total_quota': total_quota, # 总额度
'available_quota': total_quota, # 可用额度
'state': 1, # 状态 1:可用,0:冻结
} file_path = os.path.join(settings.user_info_path, '%s.json' % name) # 用户文件路径
self.write_dict(user_dict, file_path)
log_msg = '用户[%s]创建成功!' % name
get_logger(settings.admin_log_path, log_msg) def modify_quota(self):
"""
调整用户额度
:return:
"""
name = input('调整的卡号>>>')
quota = int(input('调整的额度>>>'))
file_path = os.path.join(settings.user_info_path, '%s.json' % name) # 用户文件路径
user_dict = self.read_dict(file_path)
user_dict['total_quota'] = user_dict['total_quota'] + quota # 调整总额度
user_dict['available_quota'] = user_dict['available_quota'] + quota # 调整实际额度
self.write_dict(user_dict, file_path)
log_msg = '账户[%s]额度调整成功,调整后的额度为[%s]' % (name, user_dict['total_quota'])
get_logger(settings.admin_log_path, log_msg) def frozen_user(self):
"""
冻结/解冻 账户
:return: Ture
"""
name = input('需要冻结/激活的卡号>>>')
state = int(input('1:激活/0:冻结 >>>'))
file_path = os.path.join(settings.user_info_path, '%s.json' % name) # 文件路径
user_dict = self.read_dict(file_path)
user_dict['state'] = state
self.write_dict(user_dict, file_path)
print('账号[%s]被[%s]' % (name, '激活' if user_dict['state'] == 1 else '冻结'))
log_msg = '账号[%s]被[%s]' % (name, '激活' if user_dict['state'] == 1 else '冻结')
get_logger(settings.admin_log_path, log_msg) # 日志 def check_user_info(self):
name = input('需要查看的账号>>>')
super().check_user_info(name) def run(self):
while 1:
print('\n--- 欢迎管理员[%s] ---' % self.name)
for num, value in enumerate(self.option_lis, 1):
print(num, value[0])
action = int(input('\n输入编号>>>'))
if hasattr(self, self.option_lis[action - 1][1]):
getattr(self, self.option_lis[action - 1][1])()
else:
print('\n编号有误,请重新输入\n')

basic.py

#!/usr/bin/env python3
# author:Alnk(李成果)
import json
import os
from atm.conf import settings class Basic:
"""基础类""" def write_dict(self, user_dict, file_name):
"""
写入数据
:param user_dict: 用户信息字典
:param file_name: 文件名称
:return: True
"""
with open(file_name, encoding='utf-8', mode='w') as f:
json.dump(user_dict, f)
f.flush()
return True def read_dict(self, file_name):
"""
读取数据
:param file_name: 文件名称
:return: user_dict
"""
with open(file_name, encoding='utf-8', mode='r') as f:
user_dict = json.load(f)
return user_dict def check_user_info(self, name): # 查看用户详细信息
file_path = os.path.join(settings.user_info_path, '%s.json' % name) # 文件路径
user_dict = self.read_dict(file_path)
msg = '''\n------ 账号[%s]的详细信息 ------
账号: %s
总额度: %s
可用额度: %s
状态: %s\n\n
''' % (user_dict['name'], user_dict['name'],
user_dict['total_quota'],
user_dict['available_quota'],
'激活' if user_dict['state'] == 1 else '冻结',
)
print(msg) def login_out(self):
exit('退出')

decorator.py

#!/usr/bin/env python3
# author:Alnk(李成果)
from atm.core.login import Login def outer(name, pwd): # 装饰器
def wraper(func):
def inner(*args, **kwargs):
login = Login()
ret = login.login(name, pwd)
if ret:
ret2 = func(*args, **kwargs)
return ret2 return inner return wraper

log.py

#!/usr/bin/env python3
# author:Alnk(李成果)
import logging
from logging import DEBUG def get_logger(log_file, log_msg):
# 1.创建logger函数对象
logger = logging.getLogger() # 这个参数是用户名字
# 2.创建流对象:文件流fh,屏幕流ch
fh = logging.FileHandler(log_file) # 写入日志文件
# 3.创建日志格式:这里可以创建多个日志格式
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
# 4.流对象添加格式对象
fh.setFormatter(formatter) # 文件流添加日志格式
# 5.logger对象添加流对象
logger.addHandler(fh)
# 6.设置日志打印级别
logger.setLevel(DEBUG) # 全局设置,同时设置文件流和屏幕流
#
logger.info(log_msg) # 打印INFO日志
# 在记录日志之后移除句柄,不然会重复打印日志
logger.removeHandler(fh)

login.py

#!/usr/bin/env python3
# author:Alnk(李成果)
import os
from atm.conf.settings import user_info_path, log_path
from atm.core.basic import Basic
from atm.core.log import get_logger class Login(Basic):
"""
认证类
""" def login(self, name=None, pwd=None):
if not name and not pwd:
name = input('卡号>>>:')
pwd = input('密码>>>:') file_path = os.path.join(user_info_path, '%s.json' % name)
if os.path.isfile(file_path):
user_dict = self.read_dict(file_path)
else:
print('账号或者密码错误!')
return False if name == user_dict['name'] and pwd == user_dict['pwd'] and user_dict['state'] == 1:
user_log_path = os.path.join(log_path, '%s.log' % name)
log_msg = '卡号[%s]登录成功!' % name
get_logger(user_log_path, log_msg) # 写入日志
print('登录成功!\n')
return user_dict
else:
print('登录失败,联系管理员')
return False

main.py

#!/usr/bin/env python3
# author: Alnk(李成果)
from atm.core.admin import Admin
from atm.core.login import Login
from atm.core.user import User def view():
"""
视图函数
"""
login = Login()
ret = login.login()
if ret:
if ret['role'] == 'admin':
a = Admin(ret['name'])
a.run()
elif ret['role'] == 'user':
u = User(ret['name'])
u.run()
else:
print("无效用户")
else:
print("登录失败,请重新登录")

payment.py

#!/usr/bin/env python3
# author:Alnk(李成果)
import os
from atm.conf import settings
from atm.core.decorator import outer
from atm.core.basic import Basic
from atm.core.log import get_logger
from atm.core.login import Login class PayMent(Login): # 支付功能 def payment(self, cash):
ret = self.login() # 获取登录返回值
if ret == False:
return 0 # 账号或密码错误
else:
self.name = ret['name']
file_path = os.path.join(settings.user_info_path, '%s.json' % self.name) # 用户文件路径
user_dict = self.read_dict(file_path) # 获取用户详细信息
if cash <= user_dict['available_quota']:
user_dict['available_quota'] -= cash
self.write_dict(user_dict, file_path) # 写入文件
log_file = os.path.join(settings.log_path, '%s.log' % self.name)
log_msg = '通过第三方支付[%s]元' % cash
get_logger(log_file, log_msg)
return 2 # 支付成功
else:
return 1 # 额度不够

user.py

#!/usr/bin/env python3
# author:Alnk(李成果)
import os
from atm.core.basic import Basic
from atm.conf import settings
from atm.core.decorator import outer
from atm.core.log import get_logger class User(Basic): # 用户类
option_lis = [('查看账户信息', 'check_user_info'),
('取现', 'with_draw'),
('还款', 'pay_back'),
('转账', 'transfer'),
('退出', 'login_out')] def __init__(self, name):
self.name = name
self.file_path = os.path.join(settings.user_info_path, '%s.json' % self.name) # 初始化用户信息文件路径
self.user_dict = self.read_dict(self.file_path) # 获取用户详细信息
self.log_file = os.path.join(settings.log_path, '%s.log' % self.name) # 日志文件路径 def check_user_info(self): # 查看账户信息
super().check_user_info(self.name) def with_draw(self): # 提现
print('你可提现额度为[%s]' % (float(self.user_dict['available_quota']) * (1 - settings.service_charge))) # 要手续费
quota = float(input('输入你想提现的金额 手续费为5%>>>'))
if quota <= float(self.user_dict['available_quota']) * (1 - settings.service_charge): # 提现不能超过可用额度
self.user_dict['available_quota'] -= quota * (1 + settings.service_charge) # 可用额度减去提现额度和手续费
self.write_dict(self.user_dict, self.file_path)
print('提现成功\n')
log_msg = '用户[%s]提现[%s]元 手续费[%s]' % (self.name, quota, quota * settings.service_charge)
get_logger(self.log_file, log_msg) # 写入到日志
else:
print('请输入合理的提现金额') def pay_back(self): # 还款
pay_back_quota = self.user_dict['total_quota'] - self.user_dict['available_quota'] # 还款额度
print('你需要还款的总额为[%s]' % pay_back_quota)
if pay_back_quota == 0:
print('不需要还款\n')
return False
input_quota = float(input('输入还款金额>>>'))
if 0 < input_quota <= pay_back_quota:
self.user_dict['available_quota'] += input_quota
self.write_dict(self.user_dict, self.file_path)
print('还款成功,可用额度为[%s]\n' % self.user_dict['available_quota'])
log_msg = '用户[%s]还款[%s]元' % (self.name, input_quota)
get_logger(self.log_file, log_msg) def transfer(self): # 转账
friend_card = input('输入对方卡号>>>')
money = float(input('转账金额>>>'))
if not os.path.isfile(os.path.join(settings.user_info_path, '%s.json' % friend_card)):
print('账号[%s]不存在\n' % friend_card)
return False
if self.user_dict['available_quota'] >= money:
self.user_dict['available_quota'] -= money # 转账,自己账号减少额度
self.write_dict(self.user_dict, self.file_path) # 写入自己的文件
file_path = os.path.join(settings.user_info_path, '%s.json' % friend_card) # 对方文件路径
friend_dict = self.read_dict(file_path) # 对方相信信息
friend_dict['available_quota'] += money # 对方账号增加额度
self.write_dict(friend_dict, file_path) # 对方详细信息写入文件
print('转账成功')
log_msg = '用户[%s]向账号[%s]转账[%s]元' % (self.name, friend_card, money)
get_logger(self.log_file, log_msg)
else:
print('可用余额不足\n') def run(self):
while 1:
print('\n--- 欢迎用户[%s] ---' % self.name)
for num, value in enumerate(self.option_lis, 1):
print(num, value[0])
action = int(input('\n输入编号>>>'))
if hasattr(self, self.option_lis[action - 1][1]):
getattr(self, self.option_lis[action - 1][1])()
else:
print('\n编号有误,请重新输入\n')

123456.json

{"name": "123456", "pwd": "123", "role": "user", "total_quota": 18000, "available_quota": 806.0, "state": 1}

admin.json

{"name": "admin", "pwd": "123", "role": "admin", "total_quota": 15000, "available_quota": 15000, "state": 1}

需求.txt

需求
1. 用户额度 15000或自定义
2. 实现购物商城,买东西加入 购物车,调用信用卡功能进行结账
3. 可以提现,手续费5%
4. 支持多账户登录
5. 支持账户间转账(用户A转账给用户B,A账户减钱、B账户加钱)
6. 记录每月日常消费流水
7. 提供还款功能
8. ATM记录操作日志(使用logging模块记录日志)
9. 提供管理功能,包括添加账户、用户额度,冻结账户等
10. 用户认证用装饰器 编码规范需求
1. 代码规范遵守pep8 (https://python.org/dev/peps/pep-0008/)
2. 函数有相应的注释
3. 程序有文档说明文件(README.md参考:https://github.com/csrftoken/vueDrfDemo)
4. 程序的说明文档必须包含的内容:程序的实现的功能、程序的启动方式、登录用户信息、程序的运行效果
5. 程序设计的流程图:

123456.log

2021-04-23 14:14:39,348 - INFO - 卡号[123456]登录成功!
2021-04-23 14:22:43,287 - INFO - 卡号[123456]登录成功!
2021-04-23 14:51:57,139 - INFO - 卡号[123456]登录成功!

admin.log

2021-04-23 14:26:57,269 - INFO - 卡号[admin]登录成功!

最新文章

  1. Spring对事务管理的支持的发展历程--转
  2. 加载 CSS 时不影响页面渲染
  3. 【Ext.Net学习笔记】04:Ext.Net中使用数据、Ext.Net Store的用法、Ext.Net ComboBox用法
  4. hnu Counting ones 统计1-n 二进制中1的个数
  5. (转) 解决ssh的&quot;Write failed: Broken pipe&quot;问题
  6. easyui知识累计.递增.
  7. mysql_insert_id 为什么会返回空值
  8. JS代码片段
  9. Css3渐变实例Demo(一)
  10. jquery绑定事件,解绑事件
  11. 扩展对EasyUI的校验规则
  12. JVM读书笔记PART3
  13. java web Servlet学习笔记-2 请求重定向和请求转发的区别
  14. IM-iOS退出后台接受消息,app退出后台能接收到推送
  15. 前端开发必备之Chrome开发者工具(下篇)
  16. Python入门—文件读写
  17. printf()、sprintf()、vprintf()、vsprintf()(转)
  18. [Hive安装问题]
  19. JAVA:调用cmd指令(支持多次手工输入)
  20. plsql developer 11 + Oracle 11g 开发环境setup

热门文章

  1. Spring:Spring-IOC容器、DI依赖注入简介
  2. ClouderaManager安装kafka报错
  3. buu 红帽杯 XX
  4. 使用Hugo框架搭建博客的过程 - 主题配置
  5. 一文搞懂一致性hash的原理和实现
  6. C语言:警告提示及解决方法
  7. USB数据线 单独供电
  8. C语言typedef的用法详解
  9. SpringBoot默认首页跳转设置
  10. Tomcat网站根目录设置