Python3的面向对象编程
面向对象OOP
全称Object Oriented Programming
对象作为基本单元,将数据和功能封装在里面.
实现很好的,复用性,灵活性扩展性.
面向对象时一种抽象
这里的抽象指的是,用分类的眼光看待事物的一种方法.
对一些事物进行总结,分析他们的共性,利用抽象.
能大大简化在实际中解决问题的难度.
面向对象包含两个基本的概念:
类: 定义了事物的抽象特点
对象: 对象时类的实例
面向对象的要素:
封装,继承,多态
常用系统函数
helo() 获取某对象的帮助文档
@classmethod 声明该方法为类方法,类方法也可以像静态方法那样调用,但是还是会走构造函数.
@staticmethod 声明该方法为静态方法,静态方法的原理和PHP一样,不走构造函数.
delattr() 删除指定对象的属性
setattr() 给对象某属性赋值,属性不存在则创建
getattr() 获取指定对象的指定属性
hasattr() 该对象是否有该属性
dir() 查看函数,查看类的所有方法和属性
globals() 获取当前全局函数与对象
locals() 获取当前局部函数与对象
hash() 获取某对象的hash值
id() 获取某对象的id
isinstance() 对象是否是该类的实例
issubclass() 该类(前)是否是该类(后)的子类(包括自己)
repr() 将对象转为字符串表达形式,类似于PHP的对象序列化
super() 使用该方式继承父类,可以避免钻石继承问题
定义类
# 定义类
# 老式类和新式类,通过内嵌函数type()很容易对比出差异,包括类名都有所不同
# 老式类,没有继承Object,建议不要用了
class 类名:
# 新式类,继承object,会继承很多新的属性和方法,在python3所有类都是新式类.
# 当没有要继承的父类时,应该继承object.object是一切类的父类
class 类名(object):
# 构造函数
def __init__(self, ...):
# 析构函数
def __del__(self, ..):
# 内嵌函数
# 返回一个对象的属性
dir()
# 获取对象的类型
type()
# 定义一个类同时在,类中定义属性
class Liuhao(object):
sex='male'
# 在构造函数中定义属性,类一旦被实例化同时传入值,就会该设置属性,同时也可以自定义属性的值
class Liuhao(object):
def __init__(self,name,age):
self.name = name
self.age = age
# 访问控制
不好意思,Python本身没有提供访问控制
所以需要通过自建格式,提醒自己要自觉.
下面是约定俗称的,格式
class Liuhao(object):
def __init__(self,name,age):
self.name = name # 公开, 类似PHP中的public
self._age = age # 私有属性,类似PHP的private
self.__weight=weight # 受保护的,类似PHP中的protected
注意__会改变属性名称,类内的调用不受影响.
但是在类的外部调用时,__weight会变成_Liuhao__weight
实例化该类后, 通过XXX.Liuhao.__dict__可以得知这一点.
# 获取对象所有的属性情况,键值对应
className.__dict__
函数 只是一块代码提供调用
方法 依附于一个类
Python中一切结对象
类的方法也算是类的一种属性
类的方法可以重新赋值为一个变量,如:字符串.
这时候在输出,就会发现,这个方法就是一个字符串.
如:
def Test(object):
def test(self):
# 占位符
pass
# 实例化该类
t = Test()
print(t.test)
<bound method Test.test of <__main__.Test object at 0x1081af198>>
t.test=111
print(t.test)
结果为111
方法的访问控制也是基本没有
还是要靠自觉
class Example(object):
# 共有方法
def add(self):
pass
# 私有方法
def _minus(self):
pass
# 受保护的,和属性篇的一样,只是改了个名字而已
def __multiply(self):
pass
# 装饰器 @classmethod
调用的的时候用类名,而不是某个对象
# 装饰器 @property
像访问属性一样调用方法
具体用法如下:
class Programer(object):
hobby='Player Computer'
def __init__(self,name,age,weight):
self.name = name
self._age = age
self.__sex = weight
@classmethod
def get_hobby(cls):
return cls.hobby
@property
def get_weight(self):
return self.__wight
def self_introduction(self):
return "My Name is %s \n I'm %s years old \n" % (self.name,self.age)
if __name__ =='__main__':
# 实例化,同时传参
programer = Programer('Albert',25,80)
# 打印对象所有的属性,包括继承的
print(dir(programer))
# 打印对象中设置的属性,以键值对应的方式
print(programer.__dict__)
# 直接用类名调用类的方法
print(Programer.get_hobby())
# 通过访问属性的形式调用该对象
print(programer.get_weight)
# 正常调用,对象里的方法
programer.self_introduction()
定义类的继承,如果没有要继承的,就继承object,object是一切类的父类
class DerivedClassName(BaseClassName):
继承会继承父类的属性和方法
子类的方法/属性名和父类方法/属性名相同时,会重写父类的方法.
在子类重,重写了父类的方法,同时又想在子类中该方法调用父类中的改方法
需要用到关键字super或者使用父类的名称调用
如下:
class A(object):
def method(self, arg):
pass
class B(A):
def method(self, arg):
# 调用被重写的父类中的该方法
super(B, self).method(arg)
# 或者,这样的方式,没有提现,类的继承关系
A.method(arg)
子类类型的判断
# 判断父类的类型
isinstance
# 判断是否是子类
issubclass
多继承
class DerivedClassName(Base1,Base2,Base3):
# 继承的案例
class Programer(object):
hobby='Player Computer'
def __init__(self,name,age,weight):
self.name = name
self._age = age
self.__sex = weight
@classmethod
def get_hobby(cls):
return cls.hobby
@property
def get_weight(self):
return self.__wight
def self_introduction(self):
return "My Name is %s \n I'm %s years old \n" % (self.name,self.age)
# 继承父类
class BackendProgramer(Programer):
# 重写父类的构造函数,同时多加入一个形参
def __init__(self,name,age,weight,language);
# 调用父类,同时传入参数,设置父类的属性
super(BackendProgramer, self).__init(name,age,weight)
# 设置父类中没有,而子类中有的属性
self.language = language
if __name__ =='__main__':
# 实例化,同时传参
programer = BackendProgramer('Albert',25,80,'Python')
# 打印对象的所有属性,包括继承的
print(dir(programer))
# 打印对象中设置的属性,以键值对应的方式,
# 发现,输出了父类和子类的构造函数中设置的所有属性
print(programer.__dict__)
# 判断对象的种类
print(type(programer))
# 判断Programer是不是他的父类
print(isinstance(programer,Programer))
做同一件事情,有不同的流程和反馈.就是属于多态.
多态要素:
1.继承
2.方法重写
# 多态的案例
class Programer(object):
hobby='Player Computer'
def __init__(self,name,age,weight):
self.name = name
self._age = age
self.__sex = weight
@classmethod
def get_hobby(cls):
return cls.hobby
@property
def get_weight(self):
return self.__wight
def self_introduction(self):
return "My Name is %s \n I'm %s years old \n" % (self.name,self.age)
# 继承父类
class BackendProgramer(Programer):
# 重写父类的构造函数,同时多加入一个形参
def __init__(self,name,age,weight,language);
# 调用父类,同时传入参数,设置父类的属性
super(BackendProgramer, self).__init(name,age,weight)
# 设置父类中没有,而子类中有的属性
self.language = language
# 重写父类的self_introduction
def self_introduction(self):
return "My Name is %s \n My favorite language is %s \n" % (self.name,self.language)
def introduce(programer):
# 判断参数是否是Programer的子类
if isinstance(programer,Programer):
programer.self_introduction()
if __name__ =='__main__':
# 实例化,同时传参
programer = Programer('Albert',25,80)
backend_programer = BackendProgramer('Tim',30,70,'Python')
# 调用该函数,进行调用,父类
introduce(programer)
# 调用该函数进行调用子类
introduce(backend_programer)
# 由此可见
多态的好处,正是因为多态的特性使得添加功能的时候非常的简单.
introduce只要保证,传参是Programer的子类,就调用self_introduction方法即可
不管参数具体是哪个类,哪个子类
Python魔术方法
魔术方法就是对类的定制
大部分的内嵌函数,都是有相应的魔术方法支持的,
如果想让类的方法,使用很多内嵌方法
可以定义相应的魔术方法,使得对象支持这些函数的调用
定义魔术方法前后有两条下划线
# 如构造方法,在垃圾回收时触发
def __init(self):
# 如析构方法
de __del__(self):
# 创建类的对象,是在__init__之前调用的
# __new__ 是返回一个初始对象
def __new__(cls):
# __new__ 和__init__的使用案例
class Programer(object):
# 重写父类的new
def __new__(cls,*args,**kwargs):
print('call__new__method')
print(args)
# 调用父类的new方法,来返回对象
return super(Programer,cls).__new__(cls,*args,**kwargs)
# 构造函数
def __init__(self,name,age):
print('call__init_method')
self.name = name
self.age = age
if __name__ == '__main__':
programer = Programer('Albert',25)
# 打印所有,类内设置的属性,以键值对应的形式
print(programer.__dict__)
# 通过下面的输出,一方面我们知道了,Python一切皆对象
# 另一方面__lt__等等,魔术方法可以知道,怪不得支持运算
>>> t = 'test'
>>> t == t
True
>>> dir(t)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
比较运算符
# 包含了两个对象比较的所有情况,大小等于
__cmp__(self,other)
# 等于
__eq__(self,other)
# 小于
__lt__(self,other)
# 大于
__gt__(self,other)
数字运算符
# 加法
__add__(self,other)
# 减法
__sub__(self,other)
# 乘法
__nul__(self,other)
# 除法
__div__(self,other)
逻辑运算
# 或
__or__(self,other)
# 与
__and__(self,other)
# 案例
class Programer(object):
# 构造函数
def __init__(self,name,age):
self.name=name
if isinstance(age,int):
self.age=age
else:
raise Exception('age must be int')
# 重写,判断是否相等, 通过age来判断的
def __eq__(self,other):
if isinstance(other,Programer):
if self.age==other.age:
return True
else :
return False
else:
raise Excetion('The type of object must be Programer')
# 重写,加法运算, 相加的也是age
def __add__(self,other):
if isinstance(other,Programer):
return self.age + other.age
else:
raise Exception('The type of object must be Programer')
if __name == '__main__:
p1 = Programer('Alber',10)
p2 = Programer('Tim',20)
print(p1 == p2)
print(p1 + p2)
也就是说,如果一个对象,可以被相应方法转换为字符串的话
那么他的类里面肯定是在类里面定义了,或者是,继承了相应的魔术方法
# 是把对象转换为适合人看的方式,不可以通过eval()运行
__str__
# 是把对象转换为适合机器运行的方式
# 该方法转换之后可以通过eval()来直接作为Python运行
__repr__
# 和上面两种是两种不同的对象
__Unicode__
# 展现对象的属性的内嵌方法,与之对应的对象里面就有一个__dir__模式方法来控制
# 调用__dir__的输出结果
# 比如某些时候不想展示,某些属性,就可以重写该方法
__dir__
# 案例
class Programer(object):
# 构造函数
def __init__(self,name,age):
self.name=name
if isinstance(age,int):
self.age=age
else:
raise Exception('age must be int')
# 重写
def __str__(self):
return '%s is %s years old' % (self.name,self.age)
# 重写类的属性展示方法
def __dir__(self):
return self.__dict__.keys()
if __name == '__main__':
p = Programer('alber',20)
# 通过对__str__的注释可以发现两者的巨大区别
# print()其中有一个就是使用__str__将对象转换为字符串
print(p)
# 通过对__dir__的注释可以发现两者的巨大区别
print(dir(p))
个人理解:
基本上所有的内嵌函数,在对象中都有相应的魔术方法.
当我们通过内涵函数,处理对象的时候,
如: print(className的实例的时候),就会调用该对象的魔术方法__str__
进行处理.也可能是你没有定义该魔术方法,但是继承的父类里面有.
Python的类本身没有访问控制
如果只是用__来定义属性,他也只是在属性前面加上类的名字.
并不会阻止访问.
但是通过魔术方法,可以给属性添加想要的访问控制.
设置对象属性
__setattr__(self,name,value):
# 使用方式
# 错误的使用犯法会导致无限递归
# 因为在于__setattr__已经重写的setattr
# 这时候再去调用内嵌函数setattr(),又会进入这个区间
# Python对无限递归限制为默认1000次,超过1000次就会报错
def __setattr__(self,name,value):
setattr(self,name,value)
# 正确的使用方法
def __setattr__(self,name,value):
self.__dirct__[name] = value
# 查询对象数据
# 在访问默认情况下没有被调用到的情况下
__getattr__(self,name):
# 在每次访问属性时都会被调用到,容易引起无限递归,使用的时候要小心
__getattribute__(self,name):
# 删除对象的属性
__delattr__(self,name):
# 案例
class Programer(object):
# 构造函数
def __init__(self,name,age):
self.name = name
self.age = age
# 获取属性,每次获取都会调用
def __getattribute__(self,name):
# 无限递归
# return getattr(self,name)
# 无限递归,其实说明__dict__获取时,他也会调用到__getattribute__方法
# return self.__dict__['name']
# 推荐写法,最好调用父类的方法,不会出现问题
return super(Programer, self).__getattribute__(name)
# 设置属性
def __setattr__(self,name,value):
# 无限递归
# setattr(self,name,value)
# 推荐写法
self.__dict__['name'] = value
if __name__ == '__main__':
p = Programer('Alter', 20)
print(p.name)