文章目录
一、实例属性和类属性
1、类属性
- 类属性是所有的是实例对象共用的属性
- 它可以通过实例对象和类对象来获取
- 类属性不能通过实例对象来修改!!!
- 想修改类属性时,需要访问到该类属性
2、实例属性
- 实例属性:每创建一个实例对象,都会在实例对象上添加这个属性
3、实例方法
- 实例方法:存在于类对象Person中
- python的类,带括号是实例化(如p = Person(‘张三’, 18)),不带括号是赋值(如p = Person)
- 类的实例化只有一种方式,就是实例化的时候,需要带括号,这个括号根据实际情况可以为空,也可以传参
class Person(object):
# 类属性money,是所有的是实例对象共用的属性
money = 10000
def __init__(self, name, age):
# name和age称为实例属性:每创建一个实例对象,都会在实例对象上添加这个属性
self.name = name
self.age = age
def test(self):
# test(self)是实例方法:存在于类对象Person中
print('我是test方法')
p = Person('张三', 18) # 实例对象p:根据实例创建出来的对象
print(hex(id(p))) # p指向的内存空间 0x7fe018163070
p1 = Person('李四', 23) # 实例对象p1
print(hex(id(p1))) # p1指向的内存空间 0x7fd04829c7c0
print(hex(id(Person))) # 类对象:Person类指向的内存空间 0x7fd511444170
# money是类属性,它可以通过实例对象和类对象来获取
print(p.money) # 可以通过实例对象来获取类属性 10000
print(p1.money) # 10000
print(Person.money) # 可以通过类对象来获取类属性 10000
# 类属性不能通过实例对象来修改!!!
# p.addr = '上海' # python支持动态属性,给实例对象 p 添加一个 addr 属性
p.money = 20000 # 不会修改类属性 money,而是给实例对象 p 添加了一个 money 属性
print(p.money) # 20000
print(p1.money) # 10000
print(Person.money) # 10000
# 想修改类属性时,需要访问到该类属性
p1.money = 100
Person.money = 3000
print(p.money) # 3000
print(p1.money) # 100
print(Person.money) # 3000
二、实例方法、类方法和静态方法
1、实例方法
- 当实例对象调用实例方法的时候,会自动的把实例对象 p 传递给 test 函数里 self参数;
- 类对象也可以调用实例方法,但是,不会自动的给方法的 self 传参类;
- 类对象调用实例方法的时候,需要手动的给方法的self传参,不推荐使用
2、类方法
- 使用 @classmethod 装饰器来说明;
- 有一个参数,参数是 cls,表示的类对象(def demo(cls)) ;
- cls 参数,表示的是类对象,类方法一般配合类属性使用;
- 只能访问类属性,不能访问到实例属性;
- 类方法可以通过类对象、和实例对象来调用;
- 需要使用类属性时候,使用类方法
3、静态方法
- 使用 @staticmethod 装饰器
- 函数不需要任何的参数(def foo()):;
- 可以理解为一个普通的函数,直接附着到了一个对象上;
- 默认不能够访问到类属性,不能访问到实例属性;
- 既不需要访问类属性,也不需要使用实例属性,使用静态方法
class Person(object):
__type = '人类' # 私有的类属性,只能在类内部使用,类 外部无法访问
def __init__(self, name, age):
self.name = name
self.age = age
def test(self):
print('我是{}'.format(self.name))
@classmethod
def demo(cls):
# print(cls) # <class '__main__.Person'>是一个类对象
# print('我是demo方法,我是一个类方法')
# return Person.__type
return cls.__type # 类方法demo访问类类对象的一个私有属性
# print(cls is Person)
@staticmethod
def foo():
print('我是Foo函数')
print(Person.__type) # 人类,可以通过类对象访问类属性
# print(p.name) # 不能访问到实例属性,因为创建该方法时,还没有实例化一个对象
def bar():
print('呵呵')
p = Person('jerry', 12) # 此时p指向类一段内存空间
# test方法,需要使用实例对象来调用
# 1、当实例对象调用实例方法的时候,会自动的把实例对象 p 传递给 test 函数里 self参数
p.test() # 此时self会指向p指向到这段内存空间,打印 我是jerry
# 2、类对象也可以调用实例方法,但是,不会自动的给方法的 self 传参
# 类对象调用实例方法的时候,需要手动的给方法的self传参
Person.test(p) # 不推荐使用,需要手动传参p
# p.demo() # True内存空间一样,cls和person
Person.demo() # 类方法可以通过类对象来调用
print(p.demo()) # 类方法还可以通过实例对象来调用,打印"人类"
p.foo() # 我是Foo函数,需要用实例对象或类对象来调用类方法
Person.foo() # 我是Foo函数
# print(Person.__type) # 外部访问会出错
三、__new__方法
- __new__根据类对象申请一段内存空间,并返回 return object.new(cls)
- __init__方法填入数据,自动把 __init__里的 self 参数指向第一步里申请好的内存空间
class Person(object):
def __new__(cls, *args, **kwargs):
print('__new__方法被调用了!!!')
# 根据类对象申请一段内存空间
return object.__new__(cls)
def __init__(self, name, age):
print('__init__方法被调用了!!!')
self.name = name
self.age = age
# 1. 调用 __new__ 方法申请内存
# 2. 调用 __init__ 方法填入数据,自动把 __init__ 里的 self 参数指向第一步里申请好的内存空间
# 3. 把 p 指向申请好的那块内存空间
p = Person('zhangsan', 18)
print(p) # 如果没有申请内存,打印p时为None,return后,<__main__.Person object at 0x7fafd82544f0>
三、单例设计模式
- 单例设计模式:第一次创建时候是什么样子,后续创建到所有都一样;
- 加判断是否是第一次使用逻辑后,如果是,那后续都同第一次
class Person(object):
__instance = None # 私有属性
__is_first = True # 判断是否是第一次调用
def __new__(cls, *args, **kwargs):
if not cls.__instance: # 如果类属性值为空,即if cls.instance == None,则申请一段内存空间给到instance,如果再次申请时,instance已有值,不再申请空间
cls.__instance = object.__new__(cls) # 把申请到的内存空间给到instance(类对象.类属性:可以访问到类属性)
return cls.__instance
def __init__(self, name, age):
if self.__is_first:
self.name = name
self.age = age
self.__is_first = False
def test(self):
print(Person.__is_first)
p1 = Person('zhangsan', 18) # p1先调用__new__,申请内存空间
p2 = Person('lisi', 20) # p2创建时,instance已有值,不再申请内存空间,p1和p2指向同一块内存空间
# p3 = Person('王五', 21)
print(p1 is p2) # 加了__new__方法中到一段逻辑后,结果为True,指向同一块内存空间
print(hex(id(p1))) # 0x7fe44829c4f0
print(hex(id(p2))) # 0x7fe44829c4f0
print(p1.name) # lisi zhangsan
print(p2.name) # lisi zhangsan p2指向同一块内存空间后,会调用__init__初始化,修改name为lisi,如果加了判断是否是第一次使用逻辑后,均打印zhangsan
# 单例设计模式:第一次创建时候是什么样子,后续创建到所有都一样
p2.test() # True,类属性__is_first只被使用过一次if self.__is_first:,后续改到都是实例属性
四、射击游戏
# 1、使用Gun类可以创建枪对象,枪有三个属性:
# 型号model:字符串、 杀伤力damage:整数 子弹数量bullet_count:整数,枪初始没有子弹
# 调用add_bullents方法可以增加子弹数量
# 调用shoot方法可以给参数敌人对象造成伤害
# 如果没有子弹,则提示玩家并且返回;如果有子弹,则子弹数量减少,调用敌人都hurt方法,给敌人造成伤害
# 2、使用Player类可以创建 警察对象 和 土匪对象,玩家有三个属性:
# 姓名name:字符串、 血量hp:整数 枪gun:使用Gun类创建都对象,玩家初始没有枪
# 调用hurt方法可以让玩家当前收到参数enemy_gun的伤害,具体流程如下:
# 1)玩家血量 减去 枪对象 的damage 伤害度
# 2)判断修改后的玩家血量
# 如果 血量 <= 0, 提示 玩家挂了,否则,提示 玩家受伤以及当前血量
# 调用fire方法可以向参数enemy开火,具体流程如下:
# 1)判断自己是否有武器,如果没有直接返回
# 2)检查自己的枪 是否有子弹,如果没有,自动装填子弹
# 3)让自己的枪调用shoot方法,并传递要设计的敌人对象
class Gun(object):
def __init__(self, model, damage, bullet_count=0):
self.model = model
self.damage = damage
self.bullet_count = bullet_count
def add_bullet(self, count):
self.bullet_count += count
def shoot(self, x):
if self.bullet_count <= 0:
print('没有子弹,不能开枪')
return
self.bullet_count -= 1
print('开了一枪,还剩{}发子弹'.format(self.bullet_count))
x.hurt(self)
class Player(object):
def __init__(self, name, hp, gun=None): # 初始化没有枪,gun=None
self.name = name
self.hp = hp
self.gun = gun
def hurt(self, enemy_gun):
if self.hp <= 0:
print('已经死了,就不要再鞭尸了!!!')
return
self.hp -= enemy_gun.damage
print('{}被{}打了,掉了{}血,还剩{}血'.format(self.name, enemy_gun.model, enemy_gun.damage, self.hp))
def fire(self, enemy):
if not self.gun:
print('你还没有枪!')
return
if not self.gun.bullet_count: # if self.gun.bullet_count==0
print('没有子弹,自动装填子弹!')
self.gun.add_bullet(30) # 添加30发子弹
self.gun.shoot(enemy) # 将参数传过去,shoot()也需要添加x接收参数
gun1 = Gun('m416', 10)
p1 = Player('张警官', 100, gun1) # 创建对象时,直接给gun1,也可以p1.gun=gun1
# p1.gun = gun1
p2 = Player('王土匪', 80)
# print(hex(id(gun1)))
# print(hex(id(policezhang)))
# print(hex(id(badwang)))
p1.fire(p2)
# for i in range(10):#循环,打10发子弹
# p1.fire(p2)
# 调用fire方法时,def fire(self, enemy)中self指的时p1,即谁调用指的便是谁,
# 然后走到self.gun.shoot(enemy),即self.gun=p1.gun=gun1,即gun1调用shoot()方法,def shoot(self, x)中self指gun1
# x.hurt(self)此时self指gun1,即将gun1传过去,传到hurt中
# def hurt(self, enemy_gun),即gun1指hurt中的enemy_gun,x指hurt中的self,x调用的hurt方法,x指p2王土匪
五、异常处理
1、异常处理的基本语句
try except as
import os
file_name = '05-射击游戏案例.py'
if os.path.exists(file_name): # os模块中path
file = open(file_name, encoding='utf-8')
print(file.read())
else:
print('你要操作的文件不存在')
try:
1 / 0
file = open('test.txt', encoding='utf-8')
except: # 不管有什么问题,都会到except中
print('出问题了!要操作的文件不存在')
# 处理指定的错误
try:
2 / 0
file = open('test.txt', encoding='utf-8')
except FileNotFoundError: # 只处理文件不存在到异常,执行 到2/0时,程序会崩溃
print('出问题了!文件不存在异常')
# 处理多个指定的错误
try:
1 / 0
file = open('test.txt', encoding='utf-8')
except (FileNotFoundError, ZeroDivisionError):
print('出错了!,文件不存在异常或除数为0异常')
# 出错时用变量e保存,会保存出错类型
try:
1 / 0
file = open('test.txt', encoding='utf-8')
except (FileNotFoundError, ZeroDivisionError) as e:
print(e) # division by zero
# print(type(e)) # <class 'ZeroDivisionError'>
2、finally
- try…finally…保证finally代码块里的内容一定会被执行;
- 如果一个函数里有finally语句,finally语句里的代码也一定会执行;
file = open('01-实例属性和类属性.mp4', 'rb')
try:
while True:
content = file.read(1024)
print(content)
if not content:
break
finally: # 保证finally代码块里的内容一定会被执行
print('文件被关闭了')
file.close()
def test(a, b):
try:
c = a + b
return c
finally:
return 'hehe'
# test 函数执行了多个return语句,后一个return会覆盖前一个return
print(test(1, 2)) # hehe 不是3
3、with关键字的使用
- with关键字能够自动帮忙执行 close 方法,但不能处理异常;
- with 是上下文管理器,with后面的对象,要有 __enter__和 __exit__方法;
- 不是任意的一个代码都能放到 with关键字后面;
- 如果要在 with 后面,对象必须要实现 __enter__和 __exit__方法
- 读取文件异常中断时(打开文件和关闭文件),使用with,可以自动把文件关闭;
- socket通信时(需要打开和关闭),也可以用with
with open('test.txt') as f:
f.read() # 读取文件时中断了,文件不能关闭,用了with上下文管理器后,可以自动把文件关闭
class Demo(object):
def __init__(self, name, age):
self.name = name
self.age = age
def test(self):
1 / 0
print('我是test方法')
def __enter__(self):
print('我是enter方法,我被执行了')
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('我是exit方法,我被执行了')
print('exc_type={},exc_val={},exc_tb={}'.format(exc_type, exc_val, exc_tb))
d = Demo('lisi', 18)
d.test() # 程序报错,但不会执行__enter__()和__exit__()方法
with Demo('zhangsan', 15) as d:
d.test() # 程序会报错,但会执行__enter__()和__exit__()方法
4、抛出异常
- 使用raise抛出异常:一旦执行了raise语句,raise后面的语句将不能执行
常见的异常:
ValueError
FileNotFoundError
ZeroDivisionError
TypeError
SyntaxError
StopIteration
PermissionError
NameError
ModuleNotFoundError
def do_cal(): # 函数可能会出现异常
num1 = int(input('请输入一个数字:'))
if num1 < 0:
raise ValueError # 主动抛出一个异常,ValueError是系统自带的异常
x = pow(num1, 0.5)
print(x + 5)
try:
do_cal()
except ValueError as e:
print('程序出现了错误!')
5、自定义异常
1、自定义异常
class NegativeSqrtError(BaseException): # 自定义异常NegativeSqrtError
def __str__(self):
return '负数不能开方!!!'
def do_cal():
num1 = int(input('请输入一个数字:'))
if num1 < 0:
raise NegativeSqrtError # 抛出自定义异常
x = pow(num1, 0.5)
print(x)
try:
do_cal()
except NegativeSqrtError as e: # 捕获自定义异常
# print('程序出现了错误!')
print(e)
2、例子
让用户输入密码,如果密码的长度不满足 [6,11] 位,抛出 PasswordError
打印错误信息时,输出内容是 ‘密码长度必须是6到11位!!!’
class LengthError(Exception):
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return '密码长度必须是{}到{}位!!!'.format(self.x, self.y) # 占位
def get_password():
min_length = 6
max_length = 11
password = input('请输入您的密码:')
if len(password) < min_length or len(password) > max_length or password.isdigit() == False:
# raise LengthError # 抛出异常的时候,不止可以抛出一个类对象,还可以抛出一个实例对象
raise LengthError(min_length, max_length) # 抛出实例对象,还可以传参
return password
try:
pwd = get_password()
print(pwd)
except Exception as e:
print(e)