一、面向对象
1、封装:
1、组装:将数据和操作(即属性和方法)组装到一起
2、隐藏数据:对外只暴露一些接口,通过接口访问对象
2、继承:
1、多复用,继承的便不用自己写了
2、多继承少修改,OCP-----减少冗余,增加复用
3、多态:
python不太需要了解
二、Python的类
1、定义:
1、必须使用class关键字
2、类名必须是大驼峰
3、类定义完成后就产生了一个类对象,绑定到了ClassName上
class MyClass: # 定义一个类
"""A example class"""
x = 'abc' # 类属性
def foo(self): # 类方法,self自带
return 'My Class'
print(MyClass.x)
print(MyClass.foo)
print(MyClass.__doc__)
三、类对象及类属性
1、类对象,类的定义就会生成一个类对象
2、类的属性,类定义中的变量和类中定义的方法都是类的属性
3、类变量,x是MyClass的变量
MyClass中,x和foo以及__doc__都是类的属性
foo是method方法对象,不是普通的function,它必须至少一个参数,第一个必须是self。
self纸袋当前实例本身
4、实例化:a = MyClass( )
5、__init__方法
Python类实例化后,会自动调用__init__方法,这个方法第一个参数必须是self
MyClass( )实际上调用的是__init__(self)方法,可以不定义,如果没有会在实例化后隐式调用
作用:对实例进行初始化
class MyClass:
def __init__(self):
print('init')
a = MyClass() # 这个就会调用__init__
初始化函数可以多个参数,第一个位置必须给self
class Person:
def __init__(self,name,age):
self.name = name
self.age = age
def showage(self):
print('{} is {}'.format(self.name,self.age))
tom = Person('Tom',20) # 实例化
jerry = Person('Jerry',25)
print(tom.name,jerry.age)
jerry.age += 1
print(jerry.age)
jerry.showage() # 调用方法
__init__()方法不能有返回值,也就是只能是None
6、实例对象instance
类实例化后一定会获得一个对象,就是一个实例对象,tom,jerry都是Person的实例
__init__方法的第一参数self就是指代某一个实例
class MyClass:
def __init__(self):
print('self in init = {}'.format(id(self)))
c = MyClass()
print('c = {}'.format(id(c)))--------->实例化会把c送到self的位置
7、实例变量和类变量
class Person:
age = 3
def __init__(self,name):
self.name = name
tom = Person('Tom')
jerry = Person('Jerry')
print(tom.name,tom.age)
print(jerry.name,jerry.age)
print(Person.age)
Person.age = 30
print(Person.age,tom.age,jerry.age)
实例变量是每一个实例自己的变量,是自己独有,类变量是类的变量,是类的所有实例共享的属性和方法
8、
特殊含义 | 含义 |
__name__ | 对象名 |
__class__ | 对象的类型 |
__dict__ | 对象的属性字典 |
__qualname__ | 类的限定名 |
举例:
class Person:
age = 3
def __init__(self,name):
self.name = name
print(Person.__class__)
print(sorted(Person.__dict__.items()),end='\n\n')
tom = Person('Tom')
print(tom.__class__)
print(tom.__dict__,end='\n')
print(tom.__class__.__name__)
print(tom.__class__.__dict__)
是类的,就是这个类所有实例的,其实例都可以访问,是实例的,就是这个实例自己的,通过类访问不到。
类变量是属于类的变量,这个类的实例可以共享
实例可以动态给自己增加一个属性。实例的.__dict__[变量名]和实例.变量名都可以访问到
实例的同名变量,就是实例自己的,会覆盖类的同名变量,但类的变量不变
class Person:
age = 3
height = 170
def __init__(self,name,age=18):
self.name = name
self.age = age
tom = Person('Tom')
jerry = Person('Jerry',20)
Person.age = 30
print(Person.age,tom.age,jerry.age)
print(Person.height,tom.height,jerry.height)
tom.height += 10
print(Person.height,tom.height,jerry.height)
Person.height += 15
print(Person.height,tom.height,jerry.height)
Person.weight = 70
print(Person.weight,tom.weight,jerry.weight)
print(tom.__dict__['height'])
print(tom.__dict__['weight'])
9、实例属性的查找顺序
tom.__dict__---->tom.__class__---->Person.__dict__
类变量使用全大写来命名
10、装饰一个类
def add_name(name):
def wrapper(clz):
clz.NAME = name
return clz
return wrapper
@add_name('Tom') # add_name = add_name('Tom')(Person)
class Person:
AGE = 3
print(Person.NAME)
11、类方法
class Person:
@classmethod ----->装饰器,自带的,写法如下
def class_method(cls):---->必须至少有一个参数,第一个默认cls,即类对象自身
cls.HEIGHT = 170
Person.class_method()----->需要运行一下,才能写进去
print(Person.__dict__['HEIGHT'])
cls不能调用类的实例,因为cls是class_method的
12、静态方法:
class Person:
@classmethod
def class_method(cls):
cls.HEIGHT = 170
@staticmethod
def static_method():------>静态方法
print(Person.HEIGHT)
Person.class_method()
Person.static_method()
print(Person.__dict__)
13、方法的调用
class Person:
def normal_method():
print('normal')
def method(self):
print("{}'s method".format(self))
@classmethod
def class_method(cls):
print('class = {0.__name__}.({0})'.format(cls))
cls.HEIGHT = 170
@staticmethod
def static_method():
print(Person.HEIGHT)
print(1,Person.normal_method())
# print(2,Person.method()) 这个不行
print(3,Person.class_method())
print(4,Person.static_method())
print(Person.__dict__)
tom = Person()
# print(1,tom.normal_method()) 这个不行,不能给参数
print(2,tom.method())
print(3,tom.class_method()) # 可以,但是还是Person
print(4,tom.static_method())
类几乎可以调用所有内部定义的方法,但是调用普通方法会报错,因为第一个参数必须是类的实例
实例也几乎可以调用所有的方法,普通函数的调用一般不会出现,不允许这么定义
总结:
类除了普通方法,都可以调用,普通方法需要对象的实例作为第一参数
实例可以调用所有类中定义的方法(包括类方法,静态方法),普通方法传入实例自身,静态和类方法,需要找到实例的类
四、访问控制
1、私有属性Private
class Person:
def __init__(self,name,age=18):
self.name = name
self.age = age
def growup(self,i=1):
if i > 0 and i < 150:
self.age += i
p1 = Person('Tom')
p1.growup(20) # 正常范围
print(p1.age)
p1.age = 160 # 超过范围,还突破了if逻辑
print(p1.age)
私有属性方法:使用双下划线开头的属性名,就是私有属性
class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age
def growup(self,i=1):
if 0 < i < 150:
self.__age += i
p1 = Person('Tom')
p1.growup(20)
print(p1.__age) # 这句不能访问,因为__把它隐藏了
print(p1._Person__age) # 不过依然可以访问,只是约定不访问
访问方式:新建一个函数,直接内部返回定义的__age,外部实例调用这个函数即可
class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age
def growup(self,i=1):
if 0 < i < 150:
self.__age += i
def getage(self):
return self.__age
p1 = Person('tom')
print(p1.getage())
p1.growup(20)
print(p1.getage())
2、保护变量
在变量名前使用一个下划线,成为保护变量,就是个约定
class Person:
def __init__(self,name,age=18):
self.name = name
self._age = age
tom = Person('Tom')
print(tom._age)
print(tom.__dict__)
3、私有方法
class Person:
def __init__(self,name,age=18):
self.name = name
self._age = age
def _getname(self): # 保护变量
return self.name
def __getage(self): # 私有变量
return self._age
tom = Person('Tom')
print(tom._getname()) # 下划线只是约定保护,没什么卵用
# print(tom.__getage()) # 无此属性
print(tom.__dict__)
print(tom.__class__.__dict__)
print(tom._Person__getage())
五、补丁:可以通过修改或者替换类的成员。使用者调用的方式没有改变,但是类提供的功能可能已经变了
猴子补丁,在运行时,动态替换属性,慎用
test代码:
class Person:
def get_score(self):
ret = {
'English':80,'Chinese':99,'History':90}return ret
test1代码:
def get_score(self):
return dict(name=self.__class__.__name__,English=10,Chinese=20,History=30)
test2代码:
from test import Person ---->导入Person
from test1 import get_score ---->导入修改模块
def monkeypatch4Person(): ---->定义补丁函数
Person.get_score = get_score ---->将原需要修改的地方替换成现在的函数
monkeypatch4Person() ---->运行一下
if __name__=="__main__":
print(Person().get_score()) ---->测试结果
六:属性装饰器
一般设计是:将实例的属性保护起来,不让外部访问,外部使用getter读取和setter设置信息
原理:
class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age ---->私有起来
def age(self):
return self.__age ---->外部读取实例属性信息
def set_age(self,age):
self.__age = age ---->外部修改实例属性
tom = Person('Tom')
print(tom.age())
tom.set_age(20)
print(tom.age())
实际应用方法;
property装饰器:后面跟的函数名就是以后的属性名,它就是getter,这个必须有,有了就是只读
setter装饰器:与函数名同名,且接受两个参数,第一个self,第二个要修改的值,有了它,可写
deleter装饰器:可以删除属性,很少用
class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age ---->定义一个私有属性
@property ---->装饰器私有属性
def age(self): ---->定义函数读取属性
return self.__age
@age.setter
def age(self,age): ---->定义函数修改属性
self.__age = age
@age.deleter
def age(self): ---->定义函数删除属性
del self.__age
tom = Person('Tom')
print(tom.age)
tom.age = 20
print(tom.age)
# del tom.age
# print(tom.age)
其他写法:
class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age
def getage(self):
return self.__age
def setage(self,age):
self.__age = age
def delage(self):
del self.__age
age = property(getage,setage,delage,'age property')--->这句写上后面可以直接.age
tom = Person('Tom')
print(tom.age)
tom.age = 20
print(tom.age)
del tom.age
或者;
class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age
age = property(lambda self:self.__age)
tom = Person('Tom')
print(tom.age)
七、对象的销毁:
类中可以定义__del__方法,成为析构函数(方法)
作用:销毁类的实例的时候调用,以释放占用的资源
由于python实现了垃圾回收机制,但不能确定何时执行,有必要时,使用del语句删除实例,手动调用
class Person:
def __init__(self,name,age=18):
self.name = name
self.__age = age
def __del__(self):
print('del')
tom = Person('Tom')
del tom
print(tom.__dict__)
八、类的继承
1、通过继承,不需要写代码就可以继承父类的属性和方法
class Animal:
def __init__(self,name):
self._name = name
def shout(self):
print('{} shout'.format(self.__class__.__name__))
@property
def name(self):
return self._name
a = Animal('monster')
a.shout()
class Cat(Animal):
pass
cat = Cat('garfield')
cat.shout()
print(cat.name)
class Dog(Animal):
pass
dog = Dog('ahuang')
dog.shout()
print(dog.name)
2、特殊属性和方法
特殊属性和方法 | 含义 | 示例 |
__base__ | 类的基类 | |
__bases__ | 类的基类元组 | |
__mro__ | 显示方法查找顺序,基类的元组 | |
mro() | 同上 | int.mro() |
__subclasses__() | 类的子类列表 | int.subclasses__() |
3、继承中的访问控制
class Animal:
__COUNT = 0
HEIGHT = 0
def __init__(self,age,weight,height):
self.__COUNT += 1
self.age = age
self.__weight = weight
self.HEIGHT = height
def eat(self):
print('{} eat'.format(self.__class__.__name__))
def __getweight(self):
print(self.__weight)
@classmethod
def showcount1(cls):
print(cls.__COUNT)
@classmethod
def __showcount2(cls):
print(cls.__COUNT)
class Cat(Animal):
NAME = 'CAT'
c = Cat(3,5,15)
c.eat()
print(c.HEIGHT)
c.showcount1() # 类方法,实例无法调用,相当于类调用
print(c.NAME)
print('{}'.format(Animal.__dict__))
print('{}'.format(Cat.__dict__))
print(c.__dict__)
print(c.__class__.mro())
从父类继承,自己没有的,就可以到父类中找
私有的都是不可以访问的,本质上是改了名
4、方法的重写、覆盖override
class Animal:
def shout(self):
print('Animal shout')
class Cat(Animal):
def shout(self):
print('miao')
def shout(self):
print(super())
print(super(Cat,self))
super().shout()
a = Animal()
a.shout()
c = Cat()
c.shout()
print(a.__dict__)
print(c.__dict__)
print(Animal.__dict__)
print(Cat.__dict__)
覆盖自身也是可以的
5、继承中的初始化
class Animal:
def __init__(self,age):
print('Animal init')
self.age = age
def show(self):
print(self.age)
class Cat(Animal):
def __init__(self,age,weight):
super().__init__(age)
print('Cat init')
self.age = age + 1
self.weight = weight
c = Cat(10,5)
c.show()
九、多继承
1、python3都是新式类,继承子object,新式类可以使用super
2、多继承:OCP原则,多继承,少修改,增强基类,实现多态
3、多态:在面向对象中,父类、子类通过继承练习在一起,如果一套方法,可以实现不同表现,就是多态
4、一个类继承自多个类就是多继承,它将具有多个类的特征
5、python使用MRO(method,resolution)