PYTHON 面向对象
面向对象之特殊方法
__str__
这个方法有2个注意点,
- str 必须返回字符串
- 触发这个方法是在把对象p用str(p)进行强转的时候才会触发,普通情况下 print(p)只能输出对象的内存地址.
但是有时候像orm中希望看到这个对象的一些属性,就可以这么操作
class test():
"""类的描述"""
def __init__(self,name,age):
self.name=name
self.age=age
def __str__(self):
# 这个方法必须返回字符串
return "对象self的属性name是%s,age是%s"%(self.name,self.age)
p = test("alex",18)
print(str(p)) # 对象self的属性name是alex,age是18
__call__
__call__的会在实例对象运行时候触发,比如p是实例对象,p({"age":18})在自我运行时候就出发了__call__方法
**{"age":18}会传给kwargs**
class test():
"""类的描述"""
def __init__(self,name):
self.name=name
def __call__(self, *args, **kwargs):
print(self.name,args,kwargs)
p = test("alex")
p(**{"age":18}) # alex () {'age': 18}
__new__,__init__
这2个都是类初始化的时候自动运行的.注意有2点
- __new__比__init__先运行
- 如果__new__有返回值才会运行__init__,否则不会运行__init__ 下面代码示例__new__没有返回值,所以__init__没有运行,导致实例没有name属性
class test():
"""类的描述"""
def __init__(self,name,age):
print("init开始了")
self.name=name
self.age=age
def __new__(cls, *args, **kwargs):
print("new开始了")
print(cls.__doc__)
p = test("alex",18)
print(p.name) # AttributeError: 'NoneType' object has no attribute 'name'
__new__ 更多是用在单例模式下
class Student:
_instance = None
def __init__(self, name, age):
self.name = name
self.age = age
def __new__(cls, *args, **kwargs):
# if cls._instance:
# return cls._instance # 有实例则直接返回
# else:
# cls._instance = super().__new__(cls) # 没有实例则new一个并保存
# return cls._instance # 这个返回是给是给init,再实例化一次,也没有关系
if not cls._instance: # 这是简化的写法,上面注释的写法更容易提现判断思路
cls._instance = super().__new__(cls)
return cls._instance
stu1 = Student('jack', 18)
stu2 = Student('jack', 18)
print(stu1 is stu2)
print(stu1.__dict__, stu2.__dict__)
# 原理:和方法2类似,将判断的实现方式,从类的绑定方法中转移到类的__new__中
# 归根结底都是 判断类有没有实例,有则直接返回,无则实例化并保存到_instance中。
__repr__
__dict__
查看类和对象中的所有成员
class test():
"""类的描述"""
def __init__(self,name,age):
self.name=name
self.age=age
def __str__(self):
# 这个方法必须返回字符串
return "对象self的属性name是%s,age是%s"%(self.name,self.age)
p = test("alex",18)
print(p.__dict__) # {'name': 'alex', 'age': 18}
print(test.__dict__) # {'__module__': '__main__', '__doc__': '类的描述', '__init__': , '__str__': , '__dict__': , '__weakref__': }
__doc__
每个对象都会有一个__doc__属性,用于描述该对象的作用,相当于注解
class test():
"""类的描述"""
def init(self):
pass
def run(self):
"""方法的描述"""
pass
t = test()
print(t.doc) # 类的描述
print(t.run.doc) # 方法的描述
__getitem__,__setitem__,__delitem__
这三个方法就如同字典的键值增删改查一样.
如果实例对象按照对象的增删改查操作,就会对应触发这三个内置方法,详见代码示例
class test():
"""类的描述"""
def __init__(self,name,age):
self.name=name
self.age=age
def __getitem__(self, item):
print("当前实例对象的属性有"%self.__dict__)
print(self.__dict__.get(item))
def __setitem__(self, key, value):
print("当前实例对象的属性有%s,接下来添加的key是%s,value是%s"%(self.__dict__,key,value))
# 当前实例对象的属性有{'name': 'alex', 'age': 18},接下来添加的key是home,value是北京
self.__dict__[key]=value
return self
def __delitem__(self, key):
print("要删除对象的key是%s"%key) # 要删除对象的key是name
p = test("alex",18)
# __getitem__ 示例
p["name"] # alex
p["name2"] # None
# __setitem__ 示例
p["home"]="北京"
print(p.__dict__) # {'name': 'alex', 'age': 18, 'home': '北京'}
# __delitem__ 示例
del p["name"] #触发 __delitem__的方法
__getattr__,__setattr__,__delattr__
和上面不同的是他是对象的属性增删改查的时候才会触发的, 注意__setattr__, 对init里的属性设置也会触发__setattr__的方法
class test():
"""类的描述"""
def __init__(self,name,age):
self.name=name
self.age=age
def __getattr__(self, item):
print("查询对象的%s属性了"%item)
def __setattr__(self, key, value):
print("设置一个键值对,key是%s,value是%s"%(key ,value))
def __delattr__(self, item):
print("删除对象的%s属性了" % item)
p = test("alex",18)
# __getattr__ 示例
p.name # 查询对象的name属性了
# __setattr__ 示例
p.home="北京"
#设置一个键值对,key是name,value是alex
#设置一个键值对,key是age,value是18
#查询对象的name属性了
#设置一个键值对,key是home,value是北京
# __delattr__ 示例
del p.name # 删除对象的name属性了
__enter__,__exit__
这2个方法是对with上下文管理的,从with p as fwith语句开始就执行__enter__,运行结束执行__exit__
注意这里的f是__enter__的返回值,所以f.name才能输出p的name属性
class test():
"""类的描述"""
def __init__(self,name,age):
self.name=name
self.age=age
def __enter__(self):
print("enter开始")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("eixt结束")
pass
p = test("alex",20)
with p as f:
print(f.name)
#enter开始
#alex
#eixt结束
上下文管理更多用的是对一些即开即关的服务的,比如开启一个示例查询数据库,查完以后及时关闭,避免一直后台和数据库保持连接,节约资源
class test():
"""类的描述"""
def __init__(self,name,age):
self.name=name
self.age=age
def __enter__(self):
self.conn="数据库的连接代码示例"
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
self.conn.close()
p = test("alex",20)
with p as f:
print(f.curser())
面向对象归一化
归一化指的是我们在父类中创建模板,子类在创建的时候必须要遵循父类的规则.比如下面的,我们要遵从父类Animal的规则,必须在子类创建run和eat的方法.否则无法正常实例化.
这个归一化的模块用的是abc,设置如下,而Animal这个类本身作用也只是为了限制规范,并无自己的方法,我们称为抽象类.
这种把abc.ABCMeta作为父类抽象类,自身是无法被实例化的.
import abc
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod
def run(self):
pass
@abc.abstractmethod
def eat(self):
pass
def sleep(self):
pass
class People(Animal):
def __init__(self):
pass
def wear(self):
pass
a=People()
报错信息如下:
Traceback (most recent call last):
File "D:/PyCharm 2018.2.4/test/test.py", line 1582, in
a=People()
TypeError: Can't instantiate abstract class People with abstract methods eat, run
类的属性方法隐藏
一般不想让外部调用类的方法或者属性,可以前面加__进行隐藏.但是隐藏只是改变了调用规则,并不是真正意义的禁止调用
有三大特性如下
特性1: 开头的属性或者方法 直接调用会报错,因为__的机制会把属性重新修改为_Classname__attribute的格式,具体可以查看__dict
特性2 所以虽然是隐藏,但是知道里面的修改机制,还是可以直接调用
特性3 __的方法和属性无法覆盖父类的.比如c的__type实际已经被内部转成了_China__type,而父类的是_People__type.所以他们不重名,也无法被覆盖
class People():
__type="人类"
home="地球"
def __init__(self,name):
self.name=name
def __hobby(self):
print("%s喜欢自由"%self.name)
def attr(self):
self.__hobby()
def run(self):
print("%s开始跑步"%self.name)
class China(People):
__type = "中国人"
def __init__(self,name,home):
super().__init__(name)
self.home=home
a=People("alex")
#特性1: __开头的属性或者方法 直接调用会报错,因为__的机制会把属性重新修改为_Classname__attribute的格式,具体可以查看__dict__
#a.__hobby() # AttributeError: 'People' object has no attribute '__hobby'
#print(a.__type) # AttributeError: 'People' object has no attribute '__type'
# 特性2 所以虽然是隐藏,但是知道里面的修改机制,还是可以直接调用
print(a._People__type) # 人类
# 特性3 类的内部调用可以直接调
a.attr() # alex喜欢自由
c = China("周杰伦","台湾")
# 特性3 __的方法和属性无法覆盖父类的.比如c的__type实际已经被内部转成了_China__type,而父类的是_People__type.所以他们不重名,也无法被覆盖