Python面向对象(二)
对象类型
一、isinstance(obj, cls)
检查是否obj是否是类 cls 的对象
1 class Foo(object): 2 pass 3 4 obj = Foo() 5 6 isinstance(obj, Foo)
二、issubclass(sub, super)
检查sub类是否是 super 类的派生类
1 class Foo(object): 2 pass 3 4 class Bar(Foo): 5 pass 6 7 issubclass(Bar, Foo)
可变类型vs不可变类型和深拷贝vs浅拷贝
可变类型 Vs 不可变类型
可变类型(mutable):列表,字典
不可变类型(unmutable):数字,字符串,元组
这里的可变不可变,是指内存中的那块内容(value)是否可以被改变
# -*- coding: utf-8 -*- ''' # @Datetime: 2019/02/13 # @author: Zhang Yafei ''' a = (1,2,3,'hello') b = a print(id(a)) print(id(b)) # 263034329432 # 263034329432 a = (23,46) print(a) print(b) print(id(a)) print(id(b)) # (23, 46) # (1, 2, 3, 'hello') # 990975432776 # 990975252824 a = 'l love you' b = a print(b) print(id(a)) print(id(b)) # l love you # 1082135746864 # 1082135746864 a = 'you lie' print(a) print(b) print(id(a)) print(id(b)) # you lie # l love you # 1082138333848 # 1082135746864 a = 111 b = a print(a) print(b) print(id(a)) print(id(b)) # 111 # 1880059248 # 1880062416 a = 12 print(a) print(b) print(id(a)) print(id(b)) # 12 # 111 # 1880062416 # 1880062416 a = [1,2,3] b = a print(b) print(id(a)) print(id(b)) # [1, 2, 3] # 880126268808 # 880126268808 a[1] = 22 print(a) print(b) print(id(a)) print(id(b)) # [1, 22, 3] # [1, 22, 3] # 880126268808 # 880126268808 a = {'name':'zhang','sex':0} b = a print(b) print(id(a)) print(id(b)) # {'name': 'zhang', 'sex': 0} # 75290721448 # 75290721448 a['age'] = 23 print(a) print(b) print(id(a)) print(id(b)) # {'name': 'zhang', 'sex': 0, 'age': 23} # {'name': 'zhang', 'sex': 0, 'age': 23} # 75290721448 # 75290721448# [1, 2, 3] """ 例如数值、字符串,元组(tuple不允许被更改)采用的是复制的方式(深拷贝?),也就是说当将另一个变量B赋值给变量A时, 虽然A和B的内存空间仍然相同,但当A的值发生变化时,会重新给A分配空间,A和B的地址变得不再相同 而对于像字典(dict),列表(List)等,改变一个就会引起另一个的改变,也称之为浅拷贝 """
深拷贝 Vs 浅拷贝
copy.copy() 浅拷贝
copy.deepcopy() 深拷贝
浅拷贝是新创建了一个跟原对象一样的类型,但是其内容是对原对象元素的引用。这个拷贝的对象本身是新的,但内容不是。拷贝序列类型对象(列表\元组)时,默认是浅拷贝。
深拷贝,在内存中将所有的数据重新创建一份(排除最后一层,即:python内部对字符串和数字的优化)
import copy n1 = "'zhang'/23" n2 = n1 n3 = copy.copy(n1) n4 = copy.deepcopy(n1) print(n1, n2, n3, n4) # 'zhang'/23 'zhang'/23 'zhang'/23 'zhang'/23 n1 = 'kobe' print(n1, n2, n3, n4) # kobe 'zhang'/23 'zhang'/23 'zhang'/23 n1 = {"k1": "zhang", "k2": 23, "k3": ["kobe", 42]} n2 = n1 n3 = copy.copy(n1) n4 = copy.deepcopy(n1) print(n1, n2, n3, n4) # {'k1': 'zhang', 'k2': 23, 'k3': ['kobe', 42]} {'k1': 'zhang', 'k2': 23, 'k3': ['kobe', 42]} {'k1': 'zhang', 'k2': 23, 'k3': ['kobe', 42]} {'k1': 'zhang', 'k2': 23, 'k3': ['kobe', 42]} n1['k3'].append(0) print(n1, n2, n3, n4) # {'k1': 'zhang', 'k2': 23, 'k3': ['kobe', 42, 0]} {'k1': 'zhang', 'k2': 23, 'k3': ['kobe', 42, 0]} {'k1': 'zhang', 'k2': 23, 'k3': # ['kobe', 42, 0]} {'k1': 'zhang', 'k2': 23, 'k3': ['kobe', 42]}
总结
1:对于不可变对象(如数字/字符串/元组),无论是深拷贝还是浅拷贝,内存地址和值都是相同的,没有意义; 2:对于可变的对象,如列表,字典类型的: 浅拷贝:值和地址都是相同的 深拷贝:值相同,地址不同 例如数值、字符串,元组(tuple不允许被更改)采用的是复制的方式(深拷贝?),也就是说当将另一个变量B赋值给变量A时, 虽然A和B的内存空间仍然相同,但当A的值发生变化时,会重新给A分配空间,A和B的地址变得不再相同 而对于像字典(dict),列表(List)等,改变一个就会引起另一个的改变,也称之为浅拷贝
异常处理
异常,在程序运行时出现非正常情况时会被抛出,比如常见的名称错误、键错误等。当程序发生一些错误的时候,该如何处理。即异常预处理方案。
例如:空列表弹出报错
In [26]: a = [] In [27]: a.pop() --------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-27-9c070c907602> in <module>() ----> 1 a.pop() IndexError: pop from empty list
1、捕获异常
try: pass except Exception,ex: pass
In [28]: try: ...: a.pop() ...: except IndexError as e: ...: print(e) ...: pop from empty list
二、异常种类
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x IOError 输入/输出异常;基本上是无法打开文件 ImportError 无法引入模块或包;基本上是路径问题或名称错误 IndentationError 语法错误(的子类) ;代码没有正确对齐 IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5] KeyError 试图访问字典里不存在的键 KeyboardInterrupt Ctrl+C被按下 NameError 使用一个还未被赋予对象的变量 SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了) TypeError 传入对象类型与要求的不符合 UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量, 导致你以为正在访问它 ValueError 传入一个调用者不期望的值,即使值的类型是正确的 常用异常
三、异常处理
捕获异常
s1 = 'hello' try: int(s1) except IndexError,e: print(e) except KeyError,e: print(e) except ValueError,e: print(e)
所以,对于异常的处理就是我们需要捕捉到这个异常,但是如果我们不知道的指定的异常怎么办呢?
万能异常 在python的异常中,有一个万能异常:Exception,他可以捕获任意异常,即:
1 s1 = 'hello' 2 try: 3 int(s1) 4 except Exception,e: 5 print(e)
四、异常其他结构
1 try: 2 # 主代码块 3 pass 4 except KeyError,e: 5 # 异常时,执行该块 6 pass 7 else: 8 # 主代码块执行完,执行该块 9 pass 10 finally: 11 # 无论异常与否,最终执行该块 12 pass
五、主动发现异常
1 try: 2 raise Exception('错误了。。。') 3 except Exception as e: 4 print(e)
六、自定义异常
1 class ShortInputException(Exception): 2 def __init__(self,length,atleast): 3 #super().__init__() 4 self.length = length 5 self.atleast = atleast 6 7 while True: 8 try: 9 a = input("输入一个字符串:") 10 if len(a) < 3: 11 raise ShortInputException(len(a),3) 12 except ShortInputException as result: 13 print("ShortInputException:输入的长度是%d,长度至少是:%d"%(result.length,result.atleast)) 14 break 15 else: 16 print("没有异常")
六、断言
断言,判断某个表达式的真假,真则程序继续执行,否则抛出 AssertionError 异常。
assert 条件 assert 1>2 Traceback (most recent call last): File "D:\pytho3.6\lib\site-packages\IPython\core\interactiveshell.py", line 2963, in run_code exec(code_obj, self.user_global_ns, self.user_ns) File "<ipython-input-3-1e5504265e0c>", line 1, in <module> assert 1>2 AssertionError assert 1<2 try: assert 1>2 except AssertionError: print('ok') ok
异常和断言的对比
1. 使用断言去检测程序中理论上不应该出现的情况。 2.当由用户输入或者外部环境(比如网络延迟、文件不存在等)引起的问题时,我们应抛出异常。 3。尽量避免抛出或捕获 [Base]Exception 。
单例模式
单例,顾名思义单个实例。
学习单例之前,首先来回顾下面向对象的内容: python的面向对象由两个非常重要的两个“东西”组成:类、实例 面向对象场景一: 如:创建三个游戏人物,分别是: 苍井井,女,18,初始战斗力1000 东尼木木,男,20,初始战斗力1800 波多多,女,19,初始战斗力2500
# ##################### 定义类 ##################### class Person: def __init__(self, na, gen, age, fig): self.name = na self.gender = gen self.age = age self.fight =fig def grassland(self): """注释:草丛战斗,消耗200战斗力""" self.fight = self.fight - 200 # ##################### 创建实例 ##################### cang = Person('苍井井', '女', 18, 1000) # 创建苍井井角色 dong = Person('东尼木木', '男', 20, 1800) # 创建东尼木木角色 bo = Person('波多多', '女', 19, 2500) # 创建波多多角色
面向对象场景二:
如:创建对数据库操作的公共类
- 增
- 删
- 改
- 查
View Code
实例:结合场景二实现Web应用程序
#!/usr/bin/env python #coding:utf-8 from wsgiref.simple_server import make_server class DbHelper(object): def __init__(self): self.hostname = '1.1.1.1' self.port = 3306 self.password = 'pwd' self.username = 'root' def fetch(self): # 连接数据库 # 拼接sql语句 # 操作 return 'fetch' def create(self): # 连接数据库 # 拼接sql语句 # 操作 return 'create' def remove(self): # 连接数据库 # 拼接sql语句 # 操作 return 'remove' def modify(self): # 连接数据库 # 拼接sql语句 # 操作 return 'modify' class Handler(object): def index(self): # 创建对象 db = DbHelper() db.fetch() return 'index' def news(self): return 'news' def RunServer(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) url = environ['PATH_INFO'] temp = url.split('/')[1] obj = Handler() is_exist = hasattr(obj, temp) if is_exist: func = getattr(obj, temp) ret = func() return ret else: return '404 not found' if __name__ == '__main__': httpd = make_server('', 8001, RunServer) print "Serving HTTP on port 8001..." httpd.serve_forever() Web应用程序实例
对于上述实例,每个请求到来,都需要在内存里创建一个实例,再通过该实例执行指定的方法。
那么问题来了...如果并发量大的话,内存里就会存在非常多功能上一模一样的对象。存在这些对象肯定会消耗内存,对于这些功能相同的对象可以在内存中仅创建一个,需要时都去调用,也是极好的!!!
单例模式出马,单例模式用来保证内存中仅存在一个实例!!!
通过面向对象的特性,构造出单例模式:
1 # ########### 单例类定义 ########### 2 class Foo(object): 3 4 __instance = None 5 6 @staticmethod 7 def singleton(): 8 if Foo.__instance: 9 return Foo.__instance 10 else: 11 Foo.__instance = Foo() 12 return Foo.__instance 13 14 # ########### 获取实例 ########### 15 obj = Foo.singleton()
对于Python单例模式,创建对象时不能再直接使用:obj = Foo(),而应该调用特殊的方法:obj = Foo.singleton() 。
from wsgiref.simple_server import make_server # ########### 单例类定义 ########### class DbHelper(object): __instance = None def __init__(self): self.hostname = '1.1.1.1' self.port = 3306 self.password = 'pwd' self.username = 'root' @staticmethod def singleton(): if DbHelper.__instance: return DbHelper.__instance else: DbHelper.__instance = DbHelper() return DbHelper.__instance def fetch(self): # 连接数据库 # 拼接sql语句 # 操作 pass def create(self): # 连接数据库 # 拼接sql语句 # 操作 pass def remove(self): # 连接数据库 # 拼接sql语句 # 操作 pass def modify(self): # 连接数据库 # 拼接sql语句 # 操作 pass class Handler(object): def index(self): obj = DbHelper.singleton() print(id(single)) obj.create() return 'index' def news(self): return 'news' def RunServer(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) url = environ['PATH_INFO'] temp = url.split('/')[1] obj = Handler() is_exist = hasattr(obj, temp) if is_exist: func = getattr(obj, temp) ret = func() return ret else: return '404 not found' if __name__ == '__main__': httpd = make_server('', 8001, RunServer) print "Serving HTTP on port 8001..." httpd.serve_forever()
总结:单利模式存在的目的是保证当前内存中仅存在单个实例,避免内存浪费!!!
实现单例模式的几种方式
1.使用模块
其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc
文件,当第二次导入时,就会直接加载 .pyc
文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这样做:
my_singleton.py
class Singleton(object): def foo(self): pass singleton = Singleton()
将上面的代码保存在文件 mysingleton.py
中,要使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象
from my_singleton import Singleton
2.使用装饰器
def Singleton(cls): _instance = {} def _singleton(*args, **kargs): if cls not in _instance: _instance[cls] = cls(*args, **kargs) return _instance[cls] return _singleton @Singleton class A(object): a = 1 def __init__(self, x=0): self.x = x a1 = A(2) a2 = A(3)
3.使用类
class Singleton(object): def __init__(self): pass @classmethod def instance(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance import threading def task(arg): obj = Singleton.instance() print(obj) for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start()
<__main__.Singleton object at 0x0000006FFBEAA438> <__main__.Singleton object at 0x0000006FFBEAA438> <__main__.Singleton object at 0x0000006FFBEAA438> <__main__.Singleton object at 0x0000006FFBEAA438> <__main__.Singleton object at 0x0000006FFBEAA438> <__main__.Singleton object at 0x0000006FFBEAA438> <__main__.Singleton object at 0x0000006FFBEAA438> <__main__.Singleton object at 0x0000006FFBEAA438> <__main__.Singleton object at 0x0000006FFBEAA438> <__main__.Singleton object at 0x0000006FFBEAA438>
看起来也没有问题,那是因为执行速度过快,如果在init方法中有一些IO操作,就会发现问题了,下面我们通过time.sleep模拟
我们在上面__init__方法中加入以下代码:
mport threading import time class Singleton(object): def __init__(self): time.sleep(1) @classmethod def instance(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance def task(arg): obj = Singleton.instance() print(obj) for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start()
输出结果:
<__main__.Singleton object at 0x000000D95435E400> <__main__.Singleton object at 0x000000D9543614E0> <__main__.Singleton object at 0x000000D95B7772B0> <__main__.Singleton object at 0x000000D95B961390> <__main__.Singleton object at 0x000000D95B9612B0> <__main__.Singleton object at 0x000000D95B961748> <__main__.Singleton object at 0x000000D95B9618D0> <__main__.Singleton object at 0x000000D95B9619B0> <__main__.Singleton object at 0x000000D95B961A90> <__main__.Singleton object at 0x000000D95B961B70>
问题出现了!按照以上方式创建的单例,无法支持多线程
解决办法:加锁!未加锁部分并发执行,加锁部分串行执行,速度降低,但是保证了数据安全
import time import threading class Singleton(object): _instance_lock = threading.Lock() def __init__(self): time.sleep(1) @classmethod def instance(cls, *args, **kwargs): with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance def task(arg): obj = Singleton.instance() print(obj) for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start() time.sleep(20) obj = Singleton.instance() print(obj)
<__main__.Singleton object at 0x00000064B40AE400> <__main__.Singleton object at 0x00000064B40AE400> <__main__.Singleton object at 0x00000064B40AE400> <__main__.Singleton object at 0x00000064B40AE400> <__main__.Singleton object at 0x00000064B40AE400> <__main__.Singleton object at 0x00000064B40AE400> <__main__.Singleton object at 0x00000064B40AE400> <__main__.Singleton object at 0x00000064B40AE400> <__main__.Singleton object at 0x00000064B40AE400> <__main__.Singleton object at 0x00000064B40AE400> <__main__.Singleton object at 0x00000064B40AE400>
这样就差不多了,但是还是有一点小问题,就是当程序执行时,执行了time.sleep(20)后,下面实例化对象时,此时已经是单例模式了,但我们还是加了锁,这样不太好,再进行一些优化,把intance方法,改成下面的这样就行:
@classmethod def instance(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance
import time import threading class Singleton(object): _instance_lock = threading.Lock() def __init__(self): time.sleep(1) @classmethod def instance(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): Singleton._instance = Singleton(*args, **kwargs) return Singleton._instance def task(arg): obj = Singleton.instance() print(obj) for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start() time.sleep(20) obj = Singleton.instance() print(obj) 完整代码
这种方式实现的单例模式,使用时会有限制,以后实例化必须通过 obj = Singleton.instance()
如果用 obj=Singleton() ,这种方式得到的不是单例
4.基于__new__方法实现(推荐使用,方便)
通过上面例子,我们可以知道,当我们实现单例时,为了保证线程安全需要在内部加入锁
我们知道,当我们实例化一个对象时,是先执行了类的__new__方法(我们没写时,默认调用object.__new__),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所有我们可以基于这个,实现单例模式
import threading class Singleton(object): _instance_lock = threading.Lock() def __init__(self): pass def __new__(cls, *args, **kwargs): if not hasattr(Singleton, "_instance"): with Singleton._instance_lock: if not hasattr(Singleton, "_instance"): Singleton._instance = object.__new__(cls) return Singleton._instance obj1 = Singleton() obj2 = Singleton() print(obj1,obj2) def task(arg): obj = Singleton() print(obj) for i in range(10): t = threading.Thread(target=task,args=[i,]) t.start()
<__main__.Singleton object at 0x0000009A868EE390> <__main__.Singleton object at 0x0000009A868EE390> <__main__.Singleton object at 0x0000009A868EE390> <__main__.Singleton object at 0x0000009A868EE390> <__main__.Singleton object at 0x0000009A868EE390> <__main__.Singleton object at 0x0000009A868EE390> <__main__.Singleton object at 0x0000009A868EE390> <__main__.Singleton object at 0x0000009A868EE390> <__main__.Singleton object at 0x0000009A868EE390> <__main__.Singleton object at 0x0000009A868EE390> <__main__.Singleton object at 0x0000009A868EE390> <__main__.Singleton object at 0x0000009A868EE390>
采用这种方式的单例模式,以后实例化对象时,和平时实例化对象的方法一样 obj = Singleton()
class Dog(object): __instance = None def __new__(cls): if cls.__instance == None: cls.__instance = object.__new__(cls) return cls.__instance else: #return 上一次创建对象的引用 return cls.__instance a = Dog() print(id(a)) b = Dog() print(id(b))
class Dog(object): __instance = None __init__flag = False def __new__(cls,name): if cls.__instance == None: cls.__instance = object.__new__(cls) return cls.__instance else: #return 上一次创建对象的引用 return cls.__instance def __init__(self,name): if Dog.__init__flag == False: self.name = name Dog.__init__flag = True a = Dog("旺财") print(id(a)) print(a.name) b = Dog("哮天犬") print(id(b)) print(b.name)
5.基于metaclass方式实现
1.类由type创建,创建类时,type的__init__方法自动执行,类() 执行type的 __call__方法(类的__new__方法,类的__init__方法) 2.对象由类创建,创建对象时,类的__init__方法自动执行,对象()执行类的 __call__ 方法
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): pass obj = Foo() # 执行type的 __call__ 方法,调用 Foo类(是type的对象)的 __new__方法,用于创建对象,然后调用 Foo类(是type的对象)的 __init__方法,用于对对象初始化。 obj() # 执行Foo的 __call__ 方法
元类的使用
class SingletonType(type): def __init__(self,*args,**kwargs): super(SingletonType,self).__init__(*args,**kwargs) def __call__(cls, *args, **kwargs): # 这里的cls,即Foo类 print('cls',cls) obj = cls.__new__(cls,*args, **kwargs) cls.__init__(obj,*args, **kwargs) # Foo.__init__(obj) return obj class Foo(metaclass=SingletonType): # 指定创建Foo的type为SingletonType def __init__(self,name): self.name = name def __new__(cls, *args, **kwargs): return object.__new__(cls) obj = Foo('xx')
实现单例模式
import threading class SingletonType(type): _instance_lock = threading.Lock() def __call__(cls, *args, **kwargs): if not hasattr(cls, "_instance"): with SingletonType._instance_lock: if not hasattr(cls, "_instance"): cls._instance = super(SingletonType,cls).__call__(*args, **kwargs) return cls._instance class Foo(metaclass=SingletonType): def __init__(self,name): self.name = name obj1 = Foo('name') obj2 = Foo('name') print(obj1,obj2)
案例:飞机大战,基于pygame
# -*- coding:utf-8 -*- import pygame from pygame.locals import * import time import random class BasePlane(object): def __init__(self,screen_temp,x,y,image): self.x = x self.y = y self.screen = screen_temp self.image = pygame.image.load(image) self.bullet_list = [] class HeroPlane(BasePlane): def __init__(self, screen_temp): BasePlane.__init__(self,screen_temp,210,580,"./feiji/hero1.png") #super.__init__() #爆炸效果用的如下属性 self.hit = False #表示是否要爆炸 self.bomb_list = [] #用来存储爆炸时需要的图片 self.__crate_images() #调用这个方法向bomb_list中添加图片 self.image_num = 0#用来记录while True的次数,当次数达到一定值时才显示一张爆炸的图,然后清空,,当这个次数再次达到时,再显示下一个爆炸效果的图片 self.image_index = 0#用来记录当前要显示的爆炸效果的图片的序号 def __crate_images(self): self.bomb_list.append(pygame.image.load("./feiji/hero_blowup_n1.png")) self.bomb_list.append(pygame.image.load("./feiji/hero_blowup_n2.png")) self.bomb_list.append(pygame.image.load("./feiji/hero_blowup_n3.png")) self.bomb_list.append(pygame.image.load("./feiji/hero_blowup_n4.png")) def display(self): """显示玩家的飞机""" #如果被击中,就显示爆炸效果,否则显示普通的飞机效果 if self.hit == True: self.screen.blit(self.bomb_list[self.image_index], (self.x, self.y)) self.image_num+=1 if self.image_num == 7: self.image_num=0 self.image_index+=1 if self.image_index>3: time.sleep(1) exit()#调用exit让游戏退出 #self.image_index = 0 else: self.screen.blit(self.image,(self.x, self.y)) for bullet in self.bullet_list: bullet.display() bullet.move() if bullet.judge():#判断子弹是否越界 self.bullet_list.remove(bullet) def move_left(self): self.x -= 20 def move_right(self): self.x += 20 def move_up(self): self.y -=10 def move_down(self): self.y +=10 def fire(self): """通过创建一个子弹对象,完成发射子弹""" print("-----fire----") bullet = Bullet(self.screen, self.x, self.y)#创建一个子弹对象 self.bullet_list.append(bullet) def bomb(self): self.hit = True class EnemyPlane(BasePlane): #敌机的类 def __init__(self, screen_temp): BasePlane.__init__(self,screen_temp,0,0,"./feiji/enemy0.png") self.direction = "right" #爆炸效果用的如下属性 self.hit = False #表示是否要爆炸 self.bomb_list = [] #用来存储爆炸时需要的图片 self.__crate_images() #调用这个方法向bomb_list中添加图片 self.image_num = 0#用来记录while True的次数,当次数达到一定值时才显示一张爆炸的图,然后清空,,当这个次数再次达到时,再显示下一个爆炸效果的图片 self.image_index = 0#用来记录当前要显示的爆炸效果的图片的序号 def display(self): if self.hit == True: self.screen.blit(self.bomb_list[self.image_index], (self.x, self.y)) self.image_num+=1 if self.image_num == 7: self.image_num=0 self.image_index+=1 if self.image_index>3: time.sleep(0.1) self.image = None else: self.screen.blit(self.image, (self.x, self.y)) for bullet in self.bullet_list: bullet.display() bullet.move() if bullet.judge(): self.bullet_list.remove(bullet) def __crate_images(self): self.bomb_list.append(pygame.image.load("./feiji/enemy0_down1.png")) self.bomb_list.append(pygame.image.load("./feiji/enemy0_down2.png")) self.bomb_list.append(pygame.image.load("./feiji/enemy0_down3.png")) self.bomb_list.append(pygame.image.load("./feiji/enemy0_down4.png")) def move(self): if self.direction == "right": self.x+=5 elif self.direction=="left": self.x-=5 if self.x>430: self.direction="left" elif self.x<0: self.direction="right" def fire(self): random_num = random.randint(1,100) if random_num==78: self.bullet_list.append(EnemyBullet(self.screen, self.x, self.y)) def bomb(self): self.hit = True class EnemyPlane1(BasePlane): #敌机的类 def __init__(self, screen_temp): BasePlane.__init__(self,screen_temp,439,0,"./feiji/enemy1.png") self.direction = "left" #爆炸效果用的如下属性 self.hit = False #表示是否要爆炸 self.bomb_list = [] #用来存储爆炸时需要的图片 self.__crate_images() #调用这个方法向bomb_list中添加图片 self.image_num = 0#用来记录while True的次数,当次数达到一定值时才显示一张爆炸的图,然后清空,,当这个次数再次达到时,再显示下一个爆炸效果的图片 self.image_index = 0#用来记录当前要显示的爆炸效果的图片的序号 def display(self): if self.hit == True: self.screen.blit(self.bomb_list[self.image_index], (self.x, self.y)) self.image_num+=1 if self.image_num == 7: self.image_num=0 self.image_index+=1 if self.image_index>4: time.sleep(0.1) self.image = None else: self.screen.blit(self.image, (self.x, self.y)) for bullet in self.bullet_list: bullet.display() bullet.move() if bullet.judge(): self.bullet_list.remove(bullet) def __crate_images(self): self.bomb_list.append(pygame.image.load("./feiji/enemy1_hit.png")) self.bomb_list.append(pygame.image.load("./feiji/enemy1_down1.png")) self.bomb_list.append(pygame.image.load("./feiji/enemy1_down2.png")) self.bomb_list.append(pygame.image.load("./feiji/enemy1_down3.png")) self.bomb_list.append(pygame.image.load("./feiji/enemy1_down4.png")) def move(self): if self.direction == "right": self.x+=5 elif self.direction=="left": self.x-=5 if self.x>430: self.direction="left" elif self.x<0: self.direction="right" def fire(self): random_num = random.randint(1,100) if random_num==78: self.bullet_list.append(EnemyBullet(self.screen, self.x, self.y)) def bomb(self): self.hit = True class Bullet(object): def __init__(self, screen_temp, x, y): self.x = x+40 self.y = y-20 self.screen = screen_temp self.image = pygame.image.load("./feiji/bullet.png") def display(self): self.screen.blit(self.image, (self.x, self.y)) def move(self): self.y-=20 def judge(self): if self.y<0: return True else: return False class EnemyBullet(object): def __init__(self, screen_temp, x, y): self.x = x+25 self.y = y+40 self.screen = screen_temp self.image = pygame.image.load("./feiji/bullet1.png") def display(self): self.screen.blit(self.image, (self.x, self.y)) def move(self): self.y+=5 def judge(self): if self.y>680: return True else: return False def key_control(hero_temp): #获取事件,比如按键等 for event in pygame.event.get(): #判断是否是点击了退出按钮 if event.type == QUIT: print("exit") exit() #判断是否是按下了键 elif event.type == KEYDOWN: #检测按键是否是a或者left if event.key == K_a or event.key == K_LEFT: print('left') hero_temp.move_left() #检测按键是否是d或者right elif event.key == K_d or event.key == K_RIGHT: print('right') hero_temp.move_right() elif event.key ==K_w or event.key == K_UP: print("up") hero_temp.move_up() elif event.key ==K_s or event.key == K_DOWN: print("down") hero_temp.move_down() #检测按键是否是空格键 elif event.key == K_SPACE: print('space') hero_temp.fire() elif event.key == K_b: print('b') hero_temp.bomb() def enemybomp(hero_temp,enemy_temp): for bullet in hero_temp.bullet_list: x0 = enemy_temp.x x1 = enemy_temp.x+51 if bullet.x>x0 and bullet.x<x1 and bullet.y<40 and bullet.y>0: enemy_temp.bomb() def hero_bomp(hero_temp,enemy_temp): for bullet in enemy_temp.bullet_list: x0 = hero_temp.x x1 = hero_temp.x+100 if bullet.x>x0 and bullet.x<x1 and bullet.y<680 and bullet.y>580: hero_temp.bomb() def main(): #1. 创建窗口 screen = pygame.display.set_mode((480,680),0,32) #2. 创建一个背景图片 background = pygame.image.load("./feiji/background.png") #3. 创建一个飞机对象 hero = HeroPlane(screen) #4.创建一个敌机 enemy = EnemyPlane(screen) enemy1 = EnemyPlane1(screen) while True: screen.blit(background, (0,0)) hero.display() if enemy.image: enemy.display() enemy.move() #调用敌机的移动方法 enemy.fire() #敌机开火 enemybomp(hero,enemy) if enemy1.image: enemy1.display() enemy1.move() enemy1.fire() enemybomp(hero,enemy1) hero_bomp(hero,enemy) hero_bomp(hero,enemy1) pygame.display.update() key_control(hero) time.sleep(0.01) if __name__ == "__main__": main()