1、__new__方法
这是我参与更文挑战的第10天,活动详情查看: 更文挑战
1.1 __new__的作用
总结
__new__是一种负责创建类实例的静态方法,它无需使用 staticmethod 装饰器修饰,且该方法会优先__init__初始化方法被调用。__new__必须要有返回值,返回实例化出来的实例,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例__init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值__new__方法是新式类出现的方法。__new__方法和__init__方法一样,都是在要实例化对象时调用,但是__new__方法先被调用
class GirlFriend(object):
def __new__(cls):
print("我先被执行")
# new方法中必须返回实例化出来的对象,该对象要通过object类或者父类中的__new__方法实现返回当前类的对象
return object.__new__(cls)
def __init__(self):
print("我后被执行")
GirlFriend()
# 运行结果为:
"""
我先被执行
我后被执行
"""
2、单例模式
2.1 单例是什么
举个常见的单例模式例子,一个优秀的男孩子可能会被很多女生喜欢,但是这个男孩子他只能有一个女朋友(对象),这个唯一的女朋友就是单例(唯一的对象)。
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。
2.2 创建单例-保证只有1个对象
class GirlFriend(object):
# 定义该变量用来保存第一个实例化出来的对象的内存地址
__isinstance = None
def __new__(cls):
# 第一次创建对象时,条件成立会给类属性__isinstance赋值为第一次的实例出的对象地址,等以后在创建条件永远不会成立,就修改不了改地址值,因此只会返回__isinstance保存下的对象
if not cls.__isinstance:
cls.__isinstance = super().__new__(cls)
return cls.__isinstance
a = GirlFriend()
b = GirlFriend()
print("a的内存地址为:", id(a))
print("b的内存地址为:", id(b))
运行结果:
a的内存地址为: 2096279946016
b的内存地址为: 2096279946016
2.3 创建单例时,只执行1次__init__方法
class GirlFriend(object):
# 定义该变量用来保存第一个实例化出来的对象的内存地址
__isinstance = None
# 该变量表示__init__方法的运行状态
__first = True
def __new__(cls, *args, **kwargs):
# 第一次创建对象时,条件成立会给类属性__isinstance赋值为第一次的实例出的对象地址,等以后在创建条件永远不会成立,就修改不了改地址值,因此只会返回__isinstance保存下的对象
if not cls.__isinstance:
cls.__isinstance = super().__new__(cls)
return cls.__isinstance
def __init__(self, name):
# 当第一次实例化时,对象第一次运行init方法会将类属性_first的状态修改为False,这样等以后在实例化对象,就不会在对初始化的值进行修改了,确保了单例的唯一性
if self.__first:
self.__first = False
self.name = name
a = GirlFriend("小迪")
b = GirlFriend("小薇")
print("a的内存地址为:", id(a))
print(a.name)
print("b的内存地址为:", id(b))
print(b.name)
运行结果:
a的内存地址为: 2002801034464
小迪
b的内存地址为: 2002801034464
小迪
3、python是动态语言
3.1 动态语言的定义
动态编程语言 是 高级程序设计语言 的一个类别,在计算机科学领域已被广泛应用。它是一类 在运行时可以改变其结构的语言 :例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。动态语言目前非常具有活力。例如JavaScript便是一个动态语言,除此之外如 PHP 、 Ruby 、 Python 等也都属于动态语言,而 C 、 C++ 等语言则不属于动态语言。----来自 维基百科
class Hero(object):
def __init__(self, name, hobby):
# 在init方法中只初始化了两个实例对象属性
self.name = name
self.hobby = hobby
def __str__(self):
return "我是{},爱好{}".format(self.name, self.hobby)
if __name__ == '__main__':
# 实例化一个Hero对象
yu_da_ye = Hero("于谦", "抽烟喝酒烫头")
print("对象的姓名属性:", yu_da_ye.name)
print("对象的爱好属性:", yu_da_ye.hobby)
# 对于name和hobby是对象在类中定义好的属性,都可以通过对象名.属性的形式调用出来
# 先通过 对象名.job获取job的属性,但是对于类中并没有定义这个属性,我们可以通过动态语言的特性给对象动态的添加属性
yu_da_ye.job = "说相声"
print("对象的工作属性为:", yu_da_ye.job)
运行效果
对象的姓名属性: 于谦
对象的爱好属性: 抽烟喝酒烫头
对象的工作属性为: 说相声
4、__slots__的使用
4.1 使用`__slots__`限制对象的属性
通过上一个章节的代码的演示,可以看出,python程序是可以在运行的时候给对象增加新的属性,为了限制这项操作我们需要借助__slots__属性来完成。代码案例
class Hero(object):
# 定义__slots__属性来限制对象只能具备哪些属性,__slots__属性需要赋予一个对象的属性名字的字符串的容器,该容器可以为list,tuple或者set
__slots__ = ('name', 'hobby')
def __init__(self, name, hobby):
self.name = name
self.hobby = hobby
if __name__ == '__main__':
# 实例化一个Hero对象
yu_da_ye = Hero("于谦", "抽烟喝酒烫头")
print("对象的姓名属性:", yu_da_ye.name)
print("对象的爱好属性:", yu_da_ye.hobby)
# 对于job属性来说,__slots_中没有该属性的字符串数据所以解释器报错
yu_da_ye.job = "说相声"
print("对象的工作属性为:", yu_da_ye.job)
运行结果
对象的姓名属性: 于谦
对象的爱好属性: 抽烟喝酒烫头
AttributeError: 'Hero' object has no attribute 'job'
4.2 `__slots__`只对自己所在类中进行限制,子类无法继承
class Hero(object):
__slots__ = ('name', 'hobby')
def __init__(self, name, hobby):
self.name = name
self.hobby = hobby
class USAHero(Hero):
pass
if __name__ == '__main__':
# 实例化一个Hero对象
spy = USAHero("川建国", "发推特")
print("对象的姓名属性:", spy.name)
print("对象的爱好属性:", spy.hobby)
# 对于job属性来说,__slots_中没有该属性的字符串数据所以解释器报错
spy.job = "总统"
print("对象的工作属性为:", spy.job)
运行结果
对象的姓名属性: 川建国
对象的爱好属性: 发推特
对象的工作属性为: 总统
property属性
1.可以改变类中方法调用的形式
可以是对象的方法调用时和属性调用一样
class Hero(object):
def eat(self):
print("英雄喜欢吃")
@property
def sleep(self):
print("英雄喜欢睡觉")
if __name__ == '__main__':
a = Hero()
a.eat()
a.sleep
property属性的定义和调用要注意一下几点:
- 定义时,在实例方法的基础上添加 @property 装饰器;并且仅有一个self参数
- 调用时,无需括号
2. property属性的完整使用
三种@property装饰器
#coding=utf-8
# ############### 定义 ###############
class Goods:
"""定义一个商品类
第一种装饰器:@property
第二种装饰器:@property方法名.setter
第三种装饰器:@property方法名.deleter
"""
@property
def price(self):
print('@property')
@price.setter
def price(self, value):
print('@price.setter')
@price.deleter
def price(self):
print('@price.deleter')
# ############### 调用 ###############
obj = Goods()
obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
obj.price = 123 # 自动执行 @price.setter 修饰的 price 方法,并将 123 赋值给方法的参数value
del obj.price # 自动执行 @price.deleter 修饰的 price 方法
三种装饰器的应用
class Goods(object):
def __init__(self):
# 原价
self.original_price = 100
# 折扣
self.discount = 0.8
@property
def price(self):
# 实际价格 = 原价 * 折扣
new_price = self.original_price * self.discount
return new_price
@price.setter
def price(self, value):
self.original_price = value
@price.deleter
def price(self):
del self.original_price
obj = Goods()
obj.price # 获取商品价格
obj.price = 200 # 修改商品原价
del obj.price # 删除商品原价
3.property属性的第二种使用方式:类属性方式
# property属性的第二种定义方式:类属性定义方式
class Goods(object):
def get_price(self):
print("get price...")
return 100
def set_price(self, value):
"""必须两个参数"""
print("set price...")
print(value)
def del_price(self):
print("del price")
price = property(get_price, set_price, del_price, "相关描述...")
obj = Goods()
obj.price # 自动调用第一个参数中定义的方法:get_price
obj.price = "价格" # 自动调用第二个参数中定义的方法:set_price方法,并将“价格”当作参数传入
desc = Goods.price.__doc__ # 自动获取第四个参数中设置的值:"相关描述..."
print(desc)
del obj.price # 自动调用第三个参数中定义的方法:del_price方法
property方法中有个四个参数
- 第一个参数是方法名,调用 对象.属性 时自动触发执行方法
- 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
- 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
- 第四个参数是字符串,调用 对象.属性.doc ,此参数是该属性的描述信息
类属性定义方式的案例
class Goods(object):
def __init__(self):
# 原价
self.original_price = 100
# 折扣
self.discount = 0.8
def get_price(self):
# 实际价格 = 原价 * 折扣
new_price = self.original_price * self.discount
return new_price
def set_price(self, value):
self.original_price = value
def del_price(self):
del self.original_price
PRICE = property(get_price, set_price, del_price, '价格属性描述...')
obj = Goods()
obj.PRICE # 获取商品价格
obj.PRICE = 200 # 修改商品原价
del obj.PRICE # 删除商品原价
Goods.PRICE.__doc__# 获取价格的描述信息
5、property属性-应用
5.1 私有属性添加getter和setter方法
class Money(object):
def __init__(self):
self.__money = 0
def getMoney(self):
return self.__money
def setMoney(self, value):
if value >= 0:
self.__money = value
else:
print("error:请存放正确的数值")
5.2 使用property升级getter和setter方法
class Money(object):
def __init__(self):
self.__money = 0
def getMoney(self):
return self.__money
def setMoney(self, value):
if value >=0:
self.__money = value
else:
print("error:数值不正确")
# 定义一个属性,当对这个money设置值时调用setMoney,当获取值时调用getMoney
money = property(getMoney, setMoney)
a = Money()
a.money = 100 # 调用setMoney方法
print(a.money) # 调用getMoney方法
#100
5.3 使用property取代getter和setter方法
- 重新实现一个属性的设置和读取方法,可做边界判定
class Money(object):
def __init__(self):
self.__money = 0
# 使用装饰器对money进行装饰,那么会自动添加一个叫money的属性,当调用获取money的值时,调用装饰的方法
@property
def money(self):
return self.__money
# 使用装饰器对money进行装饰,当对money设置值时,调用装饰的方法
@money.setter
def money(self, value):
if value >= 0:
self.__money = value
else:
print("error:不是整型数字")
a = Money()
a.money = 100
print(a.money)
6、魔法属性
无论人或事物往往都有不按套路出牌的情况,Python的类属性也是如此,存在着一些具有特殊含义的属性,详情如下:
6.1 __doc__
- 表示类的描述信息
class Foo:
""" 描述类信息,这是用于看片的神奇 """
def func(self):
pass
print(Foo.__doc__)
#输出:类的描述信息
6.2 __module__ 和 __class__
- module 表示当前操作的对象在那个模块
- class 表示当前操作的对象的类是什么
test.py
# -*- coding:utf-8 -*-
class Person(object):
def __init__(self):
self.name = 'laowang'
main.py
from test import Person
obj = Person()
print(obj.__module__) # 输出 test 即:输出模块
print(obj.__class__) # 输出 test.Person 即:输出类
6.3 __dict__
- 类或对象中的所有属性
类的实例属性属于对象;类中的类属性和方法等属于类,即:
class Province(object):
def __init__(self, name, count):
self.name = name
self.count = count
def func(self, *args, **kwargs):
print('func')
# 获取类的属性,即:类属性、方法、
print(Province.__dict__)
# 输出:{'__dict__': <attribute '__dict__' of 'Province' objects>, '__module__': '__main__', 'country': 'China', '__doc__': None, '__weakref__': <attribute '__weakref__' of 'Province' objects>, 'func': <function Province.func at 0x101897950>, '__init__': <function Province.__init__ at 0x1018978c8>}
obj1 = Province('山东', 10000)
print(obj1.__dict__)
# 获取 对象obj1 的属性
# 输出:{'count': 10000, 'name': '山东'}
obj2 = Province('山西', 20000)
print(obj2.__dict__)
# 获取 对象obj2 的属性
# 输出:{'count': 20000, 'name': '山西'}
6.4 __init__
- 初始化方法,通过类创建对象时,自动触发执行
class Person:
def __init__(self, name):
self.name = name
self.age = 18
obj = Person('laowang') # 自动执行类中的 __init__ 方法
6.5 __del__
- 当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,__del__的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo:
def __del__(self):
pass
6.6 __str__
- 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
class Foo:
def __str__(self):
return 'laowang'
obj = Foo()
print(obj)
# 输出:laowang
6.7 __call__(了解)
- 对象后面加括号,触发执行。
注:__init__方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
6.8__getitem__、__setitem__、__delitem__(了解)
- 用于索引操作,如字典。以上分别表示获取、设置、删除数据
# -*- coding:utf-8 -*-
class Foo(object):
def __getitem__(self, key):
print('__getitem__', key)
def __setitem__(self, key, value):
print('__setitem__', key, value)
def __delitem__(self, key):
print('__delitem__', key)
obj = Foo()
result = obj['k1'] # 自动触发执行 __getitem__
obj['k2'] = 'laowang' # 自动触发执行 __setitem__
del obj['k1'] # 自动触发执行 __delitem__
6.9__getslice__、__setslice__、__delslice__(了解)
- 该三个方法用于分片操作,如:列表
# -*- coding:utf-8 -*-
class Foo(object):
def __getslice__(self, i, j):
print('__getslice__', i, j)
def __setslice__(self, i, j, sequence):
print('__setslice__', i, j)
def __delslice__(self, i, j):
print('__delslice__', i, j)
obj = Foo()
obj[-1:1] # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__
del obj[0:2] # 自动触发执行 __delslice__
7、with关键字和上下文管理器
7.1with关键字的使用
在我们日常使用场景中,经常会操作一些资源,比如文件对象、数据库连接、Socket连接等,资源操作完了之后,不管操作的成功与否,最重要的事情就是关闭该资源,否则资源打开太多而没有关闭,程序会报错,以文件操作为例,通常我们会这样写:
f = open('file.txt', 'w')
try:
f.write("Hello")
finally:
f.close()
但既然close方法是必须的操作,那就没必要显式地调用,所以Python给我们提供了一种更优雅的方式,使用with语句:
with open('file.txt', 'w') as f:
f.write("Hello")
在退出with语句下的代码块之后,f 对象会自动执行自己的close方法,实现资源的释放,简洁优雅。
事实上,上面一段代码就用到了上下文管理器的知识。
某种程度上,上下文管理器可以理解成try/finally的优化,使得代码更加易读,在通常情况下,我们读取文件的时候,如果不适用with语句,为了防止出错,可以采用try/finally的语句来进行读取,使得文件可以正常执行close()方法。
f = open('file.text', 'w'):
try:
f.write('hello')
finally:
f.close()
很明显,with语句比try/finally更易读,更友好。
7.2上下文管理器
上下文管理器协议,是指要实现对象的 enter() 和 exit() 方法。
上下文管理器也就是支持上下文管理器协议的对象,也就是实现了 enter() 和 exit() 方法。
上下文管理器 是一个对象,它定义了在执行 with 语句时要建立的运行时上下文。 上下文管理器处理进入和退出所需运行时上下文以执行代码块。 通常使用 with 语句来使用,但是也可以通过直接调用它们的方法来使用。
简单来说,我们定义一个上下文管理器,需要在一个类里面一个实现__enter__(self)和 __exit__(self, exc_type, exc_value, traceback) 方法。
通常情况下,我们会使用with语句来使用上下文管理器:
with context_expr [as var]:
with_body
配合with语句使用的时候,上下文管理器会自动调用__enter__方法,然后进入运行时上下文环境,如果有as 从句,返回自身或另一个与运行时上下文相关的对象,值赋值给var。当with_body执行完毕退出with语句块或者with_body代码块出现异常,则会自动执行__exit__方法,并且会把对于的异常参数传递进来。如果__exit__函数返回True。则with语句代码块不会显示的抛出异常,终止程序,如果返回None或者False,异常会被主动raise,并终止程序。三 上下文管理器的运用场景
上下文管理器的典型用法包括保存和恢复各种全局状态,锁定和解锁资源,关闭打开的文件等。
7.3enter和exit方法说明
- enter方法说明 上下文管理器的enter方法是可以带返回值的,默认返回None,这个返回值通过with...as...中的 as 赋给它后面的那个变量
当然with...as...并非固定组合,单独使用with...也是可以的,上下文管理器的enter方法还是正常执行,只是这个返回值并没有赋给一个变量,with下面的代码块也不能使用这个返回值。
- exit方法说明 上下文管理器的exit方法接收3个参数exc_type、exc_val、exc_tb,如果代码块BLOCK发生了异常e并退出,这3个参数分别为type(e)、str(e)、e.traceback,否则都为None。
同样exit方法也是可以带返回值的,这个返回值应该是一个布尔类型True或False,默认为None(即False)。如果为False,异常会被抛出,用户需要进行异常处理。如果为True,则表示忽略该异常。
class abc(object):
def __enter__(self):
# 进入with 调用
pass
def __exit__(self, exc_type, exc_val, exc_tb):
# 离开with 调用
# 异常类型,异常值,异常追踪
print(exc_type)
print(exc_val)
print(exc_tb)
with abc() as a:
print("enter")
b = 1 / 0
print("exit")
7.4资源的创建和释放场景
上下文管理器的典型用法包括保存和恢复各种全局状态,锁定和解锁资源,关闭打开的文件等。
模拟一下Python中的open函数的实现过程
class File():
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
def __enter__(self):
print("entering")
self.f = open(self.filename, self.mode)
return self.f
def __exit__(self, *args):
print("will exit")
self.f.close()
with File('out.txt', 'w') as f: # f用来接收enter方法返回的对象
print("writing")
f.write('hello, python')
结语
文章篇幅较长,给看到这里的小伙伴点个大大的赞!由于作者水平有限,文章中难免会有错误之处,欢迎小伙伴们反馈指正。
如果觉得文章对你有帮助,麻烦 点赞、评论、收藏
你的支持是我最大的动力!!!