面向对象
面向对象
- 一种认识世界、分析世界的方法论。将事物抽象成类
类
- 类是抽象的概念,是事物的抽象,是一类事物的共同特征的集合(
属性
和方法
的集合)
对象
instance
、object
;是类的具象,是一个实体
哲学
一切皆对象
- 对象(类)是
数据
和操作
的封装 - 对象是独立的,对象之间可以相互作用
- OOP现有最接近人类认知的编程范式
面向对象三要素
Python类
- 定义
class ClassName:
block
- 必须使用
class
关键字 - 类名必须是用
大驼峰
命名 - 类定义完成后,即产生了一个
类对象
,绑定到标识符ClassName
类对象及类属性
- 类对象,定义类即产生一个类对象
- 类属性,类定义中的变量和类定义的方法都是类的属性
- 类变量,上图中
x
是类MyClass
的变量 foo
是方法对象method,不是普通的函数对象function,一般要求至少有一个参数(可以是self,只是惯用标识符,可换名字)。self
指代当前实例本身
实例化
- 语法:
a = MyClass()
- 实例化只能创建一个该类实例(对象),新的对象
- 实例化:
__new__
→ 实例化后会自动调用初始化方法。 - 初始化:
__init__(self)
→(return None)
,首个参数必须是self
,其他参数随意,可以不定义,在实例化后隐式调用 - 方法属于类
实例对象instance
- 类实例化后必获得一对象,就是实例对象
- 实例对象会绑定方法,调用方法时采用instance.function();函数签名是function(self),self即为instance,function保存在instance中,而不是class中。称为实例变量
实例变量是每个实例独有的;类变量是类的变量,是类的所有实例共享的属性和方法
Python中每个对象都有不同的属性。函数、类都是对象,类的实例也是对象
self
实例变量和类变量
- 实例变量是每个实例独有的;类变量是类的变量,是类的所有实例共享的属性和方法
class Person:
age = 3
def __init__(self,name):
self.name = name
tom = Person('tom',22) # __init__() takes 2 positional arguments but 3 were given
tom = Person('tom') # 实例化,初始化
jax = Person('jax')
print(tom.name,tom.age) # tom 3
print(jax.name,jax.age) # jax 3
print(Person.age) # 3
Person.age = 30 # 赋值重新定义变量
print(Person.age,tom.age,jax.age) # 30 30 30
属性访问顺序
- 所有对象可以动态给自己增加一个属性。
实例.__dict__[变量名]
和实例.变量名
都可以访问 - 访问属性
- 类的属性共享,实例的属性不共享
- 实例属性的查找顺序
- 实例使用
.
点号访问属性,先找自己的字典__dict__
;若没有,则通过属性__class__
找自己的类,然后去类中的字典__dict__
中找 - 若使用
__dict__[变量名]
访问,则不用以上方式查找。此为指明使用字典的key
查找,不是属性查找
装饰一个类
- 增加类变量
def add(name,cls):
cls.NAME = name # 动态增加类属性
- 增加类变量改进成装饰器
def add_name(name='TOM'):
def wrapper(cls):
cls.NAME = name
return cls
return wrapper
@add_name(name='JAX') # Person = wrapper(Person)
class Person:
AGE = 3
print(Person.NAME) # JAX
print(Person.AGE) # 3
类方法
- 通过
cls
可以直接操作类的属性,无法操作类的实例
class Pe:
@classmethod
def class_method(cls):
print('class={0.__name__}({0})'.format(cls)) # class = Pe(<class '__main__.Pe'>)
cls.h = 111
Pe.class_method()
print(Pe.__dict__) # {...}
静态方法
- 调用时不会隐式的传入参数
class P:
@classmethod
def class_method(cls):
print('class={0.__name__}({0})'.format(cls)) # class=P(<class '__main__.P'>)
cls.h = 123
@staticmethod
def static_method():
print(P.h) # 123
P.class_method()
P.static_method()
print(P().static_method()) # None
print(P.__dict__) # {...}
- 普通方法:实例调用方法时会将自身作为第一参数传入(
self
),类调用时则不传入参数 - 类方法:添加装饰器后,调用时都会将其类作为第一参数传入(
cls
) - 静态方法:调用时不会将实例&类作为第一参数传入(*any)
- 类未实例化则无实例,有类未必有实例,有实例必有类
私有属性
- 私有属性(使用双下划线开头的属性名)
class Person:
def __init__(self,name,age=11):
self.name = name
self.__age = age
def growup(self,i=1):
if i > 0 and i < 100:
self.__age += i
p1=Person('tom')
p1.growup(22) # 33
p1.__age=123
print(p1.__age) # 'Person' object has no attribute '__age'
- 访问私有变量(双下划线声明实例变量时解释器会将其改名为‘
_类名__变量名
’)
class Person:
def __init__(self,name,age=11):
self.name = name
self.__age = age
def growup(self,i=1):
if i > 0 and i < 100:
self.__age += i
def getage(self):
return self.__age
p1 = Person('tom')
p1.__age = 25
print(p1.__age) # 25
print(p1.getage()) # 11
print(p1.__dict__) # {'name': 'tom', '_Person__age': 11, '__age': 25}
- 保护变量(变量名前使用一个下划线)
class Person:
def __init__(self,name,age=17):
self.name = name
self._age = age
tom = Person('Tom')
print(tom._age) # 17
print(tom.__dict__) # {'name': 'Tom', '_age': 17}
- 属性装饰器
class P:
def __init__(self,name,age=15):
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
print('del')
tom = P('Tom')
print(tom.age) # 15
tom.age = 23
print(tom.age) # 23
del tom.age # del
面向对象例题
- 随机整数生成类,指定生成的个数、范围、每批生成的个数
method 1:普通类实现
import random
class RandomGen:
def __init__(self,start=1,stop=100,patch=10): # 实例化初始化,提供类属性
self.start = start
self.stop = stop
self.patch = patch
def generate(self):
return [random.randint(self.start, self.stop) for x in range(self.patch)]
a = RandomGen()
print(a.generate())
method 2:类方法,作为工具实现
import random
class RandomGen:
@classmethod # 类方法
def generate(self,start=1,stop=100,patch=10):
return [random.randint(start, stop) for x in range(patch)]
a = RandomGen()
print(a.generate())
method 3:生成器实现version 1
import random
class RandomGenerator:
def __init__(self,start=1,stop=100,patch=10): # 实例化初始化,提供类属性
self.start = start
self.stop = stop
self.patch = patch
self._gen = self._generate()
def _generate(self):
while 1:
yield random.randint(self.start, self.stop) # 返回一个随机数
def generate(self,count=0):
patch = self.patch if count<=0 else count # 指定生成的次数,未指定则用缺省值(类属性patch=10)
return [next(self._gen) for x in range(patch)] # 返回指定次数的随机数
a = RandomGenerator()
print(a.generate())
print(a.generate(5))
method 4:生成器实现version 2
import random
class RandomGenerator:
def __init__(self,start=1,stop=100,patch=10):
self.start = start
self.stop = stop
self.patch = patch
self._gen = self._generate()
def _generate(self):
while 1: # yield一批数据
yield [random.randint(self.start, self.stop) for _ in range(self.patch)]
def generate(self,count=0):
if count > 0: # 控制次数
self.patch = count
return next(self._gen)
a = RandomGenerator()
print(a.generate())
print(a.generate(5))
method 5:使用property
import random
class RandomGenerator:
def __init__(self, start=1, stop=100, patch=10):
self.start = start
self.stop = stop
self.patch = patch
self._gen = self._generate()
def _generate(self):
while 1:
yield [random.randint(self.start, self.stop) for _ in range(self.patch)]
def generate(self):
return next(self._gen)
@property
def patch(self):
return self._patch
@patch.setter
def patch(self,value):
self._patch = value
a = RandomGenerator()
print(a.generate())
a.patch = 5
print(a.generate())
- 上题中的类随机生成20个数配对二维坐标并打印
import random
class RandomGen:
@classmethod # 类方法
def generate(self, start=1, stop=100, patch=10):
return [random.randint(start, stop) for x in range(patch)]
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
a = RandomGen()
points = [Point(x,y) for x, y in zip(a.generate(),a.generate())] # zip函数配对组成二维
for p in points:
print('({}):({})'.format(p.x, p.y),end = '\t')
继承
- 语法:
class 子类名(基类1[,基类2,...]):
block
- 从父类继承,括号中为继承的类的列表
- 父类,称为基类,超类
- 子类,称为派生类
- 支持多继承,继承可以多级
- 类定义时,若无基类,则继承自
object
(在Python3中object
是所有对象的根基类)
class A:
pass
等价于
class A(object)
pass
__base__
# 类的基类__bases__
# 类的基类元组__mro__
#显示方法查找顺序,基类的元组mro()
# 同上,返回列表__subclasses__()
# 类的子类列表
继承中的访问控制
- 属性查找顺序
实例的__dict__
→类__dict__
→父类__dict__
- 查找完成后若找到则
立即返回
,未找到则返回异常
- 继承时,
公有的
,子类和实例都可以访问
;私有的
,子类和实例不可直接访问
,但私有变量所在的类内的方法中可以访问这个私有变量
方法的重写覆盖
super()
可以访问到父类的属性
class Animal:
def shout(self):
print('Animals shouts')
class Cat(Animal):
# 覆盖了父类方法
def shout(self):
print('miao')
# 覆盖了自身的方法,显式调用了父类的方法
# super()可以访问到父类的属性
def shout(self):
print(super()) # <super: <class 'Cat'>, <Cat object>>
print(super(Cat, self)) # <super: <class 'Cat'>, <Cat object>>
super().shout()
super(Cat,self).shout() # Animals shouts ,等价于super()
a = Animal()
a.shout() # Animals shouts
c = Cat()
c.shout() # miao
print(a.__dict__) # {}
print(c.__dict__) # {}
print(Animal.__dict__) # {'__module__': '__main__', 'shout': <function Animal.shout at 0x0000000537EFDE18>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
print(Cat.__dict__) # {'__module__': '__main__', 'shout': <function Cat.shout at 0x0000000537EFDEA0>, '__doc__': None}
class A:
def __init__(self,age):
print('A init')
self.age = age
def show(self):
print(self.age)
class B(A):
def __init__(self,age,w):
# 调用父类的__init__方法顺序决定show方法的结果
super().__init__(age) # A.__init__(age)
print('B init')
self.age = age + 1
self.w = w
b = B(10,5)
b.show()