python-内存管理机制
1、python 内存管理机制
gc机制:garbage collection 引用计数机制为主,标记清除和隔代回收为辐。
(1)引用计数
引用计数也是一种垃圾收集机制,而且是一种最直观、最简单的垃圾收集技术。党某个对象的引用计数降为0时,说明没有任何引用指向该对象,该对象就成为要回收的垃圾,被回收掉。但是有一个例外,循环引用是对象之间的相互引用,会使得一组对象的引用计数不为0,然后这些对象实际上没有被任何外部对象所引用,这些对象就会占内存永远不会被释放掉。
因此Python又引入了其他的垃圾回收机制来弥补引用计数的缺陷:“标记-清理”,“分代回收”。
(2)标记清理
对于循环引用,当两个对象相互引用时,del语句可以减少a和b的引用计数,并销毁用于引用底层对象的名称。然而由于每个对象都包含对其他对象的引用,因此引用计数不会归零,对象也不会销毁;从而导致内存泄漏。为了解决这一问题,解释器会定期执行一个循环检测器,搜索不可访问的对象的循环并删除它们。
在实际操作中并不改动真实的引用计数,而是将集合中对象的引用计数复制一份副本,改动该对象引用的副本。(对于副本做任何的改动,都不会影响到对象生命周期的维护。)这个计数副本的唯一作用是寻找root object集合(该集合中的对象是不能被回收的)。当成功寻找到root object集合之后,首先将现在的内存链表一分为二,一条链表中维护root object集合,成为root链表,而另外一条链表中维护剩下的对象,成为unreachable链表。之所以要剖成两个链表,是基于这样的一种考虑:现在的unreachable可能存在被root链表中的对象直接或间接引用的对象,这些对象是不能被回收的,一旦在标记的过程中,发现这样的对象,就将其从unreachable链表中移到root链表中;当完成标记后,unreachable链表中剩下的所有对象就是名副其实的垃圾对象了,接下来的垃圾回收只需限制在unreachable链表中即可。
(3)分代回收
从前面“标记-清除”这样的垃圾收集机制来看,这种垃圾收集机制所带来的额外操作实际上与系统中总的内存块的数量是相关的,当需要回收的内存块越多时,垃圾检测带来的额外操作就越多,而垃圾回收带来的额外操作就越少;反之,当需回收的内存块越少时,垃圾检测就将比垃圾回收带来更少的额外操作。
举个例子来说明:
当某些内存块M经过了3次垃圾收集的清洗之后还存活时,我们就将内存块M划到一个集合A中去,而新分配的内存都划分到集合B中去。当垃圾收集开始工作时,大多数情况都只对集合B进行垃圾回收,而对集合A进行垃圾回收要隔相当长一段时间后才进行,这就使得垃圾收集机制需要处理的内存少了,效率自然就提高了。在这个过程中,集合B中的某些内存块由于存活时间长而会被转移到集合A中,当然,集合A中实际上也存在一些垃圾,这些垃圾的回收会因为这种分代的机制而被延迟。
2、引用计数机制的优点:
简单
实时性:一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。
实时性还带来一个好处:处理回收内存的时间分摊到了平时。
引用计数机制的缺点:
维护引用计数消耗资源
循环引用,解决不了
list1 = []
list2 = []
list1.append(list2)
list2.append(list1)
list1与list2相互引用,如果不存在其他对象对它们的引用,list1与list2的引用计数也仍然为1,
所占用的内存永远无法被回收,这将是致命的。 对于如今的强大硬件,缺点1尚可接受,
但是循环引用导致内存泄露,注定python还将引入新的回收机制。(标记清除和分代收集)
GC系统所承担的工作远比"垃圾回收"多得多。实际上,它们负责三个重要任务。它们
1. 为新生成的对象分配内存
2. 识别那些垃圾对象
3. 从垃圾对象那回收内存。
3、单元测试
import unittest def login(username, password): if username == 'admin123' and password == '123456': return True else: return False class TestStringMethods(unittest.TestCase): def test_strip(self): print('----->strip') s = ' hello ' self.assertEqual(s.strip(), 'hello') def test_upper(self): print('----->upper') self.assertEqual('abc'.upper(), 'ABC') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) def test_split(self): s = 'hello world' self.assertEqual(s.split(), ['hello', 'world']) # check that s.split fails when the separator is not a string with self.assertRaises(TypeError): s.split(2) def test_login(self): self.assertTrue(login('admin', '123456')) def setUp(self): print('--------start-----------') def tearDown(self): print('----------end------------') if __name__ == '__main__': unittest.main()
4、文档测试
''' 文档注释: ''' def factorial(n): """Return the factorial of n, an exact integer >= 0. >>> [factorial(n) for n in range(6)] [1, 1, 2, 8, 24, 120] >>> factorial(30) 265252859812191058636308480000000 >>> factorial(-1) Traceback (most recent call last): ... ValueError: n must be >= 0 Factorials of floats are OK, but the float must be an exact integer: >>> factorial(30.1) Traceback (most recent call last): ... ValueError: n must be exact integer >>> factorial(30.0) 265252859812191058636308480000000 It must also not be ridiculously large: >>> factorial(1e100) Traceback (most recent call last): ... OverflowError: n too large """ import math if not n >= 0: raise ValueError("n must be >= 0") if math.floor(n) != n: raise ValueError("n must be exact integer") if n + 1 == n: # catch a value like 1e300 raise OverflowError("n too large") result = 1 factor = 2 while factor <= n: result *= factor factor += 1 return result if __name__ == "__main__": import doctest doctest.testmod()
5、银行管理系统:
# __author:gang # date: 2019/8/16 ''' 银行管理: card:类 :cardId password money user 类: username phone id cards[] 银行的工作人员: 账号 密码 ''' import pickle import time import random class Card: def __init__(self, cardid, password, money): self.cardid = cardid self.password = password self.money = money self.cardlock = False def __str__(self): return '卡号:{},余额:{}'.format(self.cardid, self.money) # 创建用户类 class User: def __init__(self, uid, name, phone, card): self.uid = uid self.name = name self.phone = phone self.card = card def __str__(self): return '用户名:{} 联系方式:{}'.format(self.name, self.phone) class BankWorker: __login_name = 'admina' __password = 'icbc123' def printWelcomePage(self): print('-' * 50) print(' 欢迎使用icbc管理系统 ') print('-' * 50) def workerLogin(self): login_name = input('输入你的账户名:') password = input('输入密码:') if self.__login_name != login_name: print('账户名输入有误!') return -1 if self.__password != password: print('密码输入有误!!!') return -1 print('登录成功!!!') time.sleep(2) return 0 def bankFunction(self): s = ''' 功能如下: 1、 开户 2、 取钱 3、 存钱 4、 转账 5、 销户 6、 查账 7、 解锁 8、 改密 9、 修改信息 10、 退出 ''' print(s) # 创建Bank类 class Bank: def __init__(self, users): self.users = users def create_user(self): # uid name phone card uid = input('输入你的身份证号码:') name = input('输入你的姓名:') phone = input('输入你的手机号码:') # 对card进行操作 # 产生卡号 cardid = self.generate_cardid() # 设置默认密码 password = input('请输入密码:') repassword = input('再次输入密码:') if password == repassword: print('密码设置成功!') else: print('两次密码不一致') return -1 # money card = Card(cardid, password, money=0) user = User(uid, name, phone, card) # 通过字典将卡号与用户进行绑定 self.users[cardid] = user print('恭喜{}开户成功!!卡号:{}'.format(name, cardid)) def generate_cardid(self): no = '' for i in range(5): n = random.randint(0, 9) no += str(n) return no def save_money(self): islogin, user = self.user_login() if not islogin: return -1 # 输入存款金额 money = int(input('验证成功!请输入存款金额:')) # 存钱成功 if money <= 100: print('存款金额必须大于100元,存款失败') return -1 user.card.money += money print('存款:{} 余额为:{}'.format(money, user.card.money)) def get_money(self): islogin, user = self.user_login() if not islogin: return -1 # 取钱 # 输入取款金额 money = int(input('验证成功!请输入取款金额:')) if money <= 100: print('取款金额必须大于0!!取款失败') return -1 if money > user.card.money: print('有卡余额不足,请充值!!!') return -1 user.card.money -= money print('您已经取款:{} 余额为:{}'.format(money, user.card.money)) def transfer_money(self): islogin, user = self.user_login() if not islogin: return -1 # 输入转账金额 money = int(input('验证成功!请输入转账金额:')) if money <= 0 or money > user.card.money: print('输入的金额有误或者账户金额不足,转账失败') return -1 cardid = input('请输入收款人的卡号:') other_user = self.users.get(cardid) # 判断有没有用户 if not user: print('不存在此卡号!转账失败') return -1 # 如果有这个用户,判断是否被锁定 if user.card.cardlock: print('该卡已经被锁定,无法完成转账...') return -1 print('转账中...') user.card.money -= money other_user.card.money += money time.sleep(1) print('转账成功:{},卡中余额为:{}'.format(money, user.card.money)) def user_login(self): cardid = input('请输入银行卡号:') user = self.users.get(cardid, None) # 判断有没有用户 if not user: print('不存在此卡号!') return False, user # 如果有这个用户,判断是否被锁定 if user.card.cardlock: print('该卡已经被锁定,请到柜台解锁...') return False, user # 没有锁定,输入密码,如果密码输入错误三次则锁定 r = self.check_password(user.card.password) if not r: # 锁定账户 print('密码连续输入3次,此卡已经被锁定') return False, user return True, user def check_password(self, password): for i in range(3): pwd = input('输入登录密码:') if pwd == password: return True return False def update_password(self): islogin, user = self.user_login() if not islogin: return -1 # 修改密码 new_pwd = input('输入新密码:') renew_pwd = input('再次输入新密码:') if new_pwd and renew_pwd: if not new_pwd or not renew_pwd: print('密码不能为空,修改密码失败!!') return -1 if new_pwd != renew_pwd: print('两次密码不一致,修改失败') return -1 user.card.password = new_pwd print('修改密码成功!!!') def del_user(self): islogin, user = self.user_login() if not islogin: return -1 answer = input('确认要注销用户吗?y/n') if answer == 'y': del self.users[user.card.cardid] time.sleep(1) print('账户销户成功!!!') def select_user(self): islogin, user = self.user_login() if not islogin: return -1 print(user) print('{}的账户金额是:{}'.format(user.card.cardid, user.card.money)) def unlock_user(self): cardid = input('输入银行卡号:') user = self.users.get(cardid, None) # 有没有这个卡号对应用户 if not user: print('不存在此卡号,存款失败!') return -1 uid = input('输入身份证号码:') if user.uid != uid: print('无效的身份证号码!解锁失败!') return -1 user.card.cardlock = False print('解锁成功!') # 入口 def main(): # 1.创建银行的工作人员类 bworker = BankWorker() # 2.加载欢迎页面 bworker.printWelcomePage() # 3.登录 r = bworker.workerLogin() if r == -1: return -1 # 加载数据库中的数据: with open('users.txt', 'rb') as rs: users = pickle.load(rs) # users = {} bank = Bank(users) while True: bworker.bankFunction() choice = input('请选择你的功能:') if choice == '1': # 开户 print('开户功能!!') bank.create_user() elif choice == '2': # 取钱 bank.get_money() elif choice == '3': # 存钱 print('------存钱--------') bank.save_money() elif choice == '4': # 转账 bank.transfer_money() elif choice == '5': print('------销户-----') bank.del_user() elif choice == '6': print('-----查账-----') bank.select_user() elif choice == '7': print('-----解锁-----') bank.unlock_user() elif choice == '8': print('------修改密码------') bank.update_password() elif choice == '9': pass elif choice == '10': # 退出功能 print('退出') answer = input('是否退出:y/n:') if answer.lower() == 'y': with open('users.txt', 'wb')as ws: pickle.dump(bank.users, ws) print('系统正在退出...') time.sleep(1) print('退出成功!!') break else: print(88) if __name__ == '__main__': main()