22. 面向对象-初始
一、面向对象书写
class Car:
# self表示当前类对象,当前你创建的是谁,谁来访问这个方法,self就是谁
def __init__(self, color, displacement, number):
self.color = color
self.displacement = displacement
self.number = number
def run(self, speed):
print(f"这辆车可以跑{speed}迈")
#实例化:类名加括号就是实例化,会自动触发__init__函数的运行,可以用它来为每个实例定制自己的特征
car = Car("red", "1.8T", "京A88888")
car.run(100)
1. __new__(cls, *args, **kwargs)
__new__ 负责对象的创建而 __init__ 负责对象的初始化;至少需要传递一个参数cls,必须要有返回值,返回实例化出来的实例。可以return父类通过super(当前类名,cls).__new__出来的实例,或者直接是object的__new__出来的实例。
2. __init__() - 构造方法
__init__()在__new()的基础上完成初始化动作,不需要返回值;有一个参数self,该self参数就是__new__()返回的实例;
3. 实例
class B():
def __new__(cls, *args, **kwargs):
print("__new__方法被执行")
return super(B, cls).__new__(cls)
def __init__(self):
print("__init__方法被执行")
b = B()
二、继承
1、新式类与经典类
- 新式类:继承了object类以及该类的子类都是新式类;Python3中如果没有继承任何类,则默认继承object类。因此Python3中都是新式类(广度优先遍历)
- 经典类:没有继承object类以及该类的子类,都是经典类;在Python2中如果一个类没有继承任何类,不会继承object类(深度优先遍历)
2、抽象与派生
(1) 抽象:通过抽象可以得到类,抽象是一种分析的过程。比如,猪八戒、小猪佩奇可以抽象出一个类,就是猪类;接着还可以从猪类、狗类抽象出动物类。先分析、抽象之后,就可以通过继承,在程序上实现这个结构。
(2) 派生:就是在子类继承父类的属性的基础上,派生出自己的属性。子类有不同于父类的属性,这个子类叫做派生类。
class Animals:
def __init__(self, name):
self.name = name
def walk(self):
print('我会走')
class Dog(Animals):
# Dog类派生出bite功能
# 派生:狗有咬人的技能
def bite(self):
print('我会咬人')
3、组合
class Mobile():
def __init__(self, color):
self.color = color
def call(self):
print("打电话")
class People():
def __init__(self, name, mobile):
self.name = name
self.mobile = mobile
mobile = Mobile("red")
people = People("Tom", mobile)
people.mobile.call()
4、属性查找顺序
对象自己->所在类中->找父类->父类的父类->Object
5、多继承
class ParentOne:
pass
class ParentTwo:
pass
class SubClass(ParentOne, ParentTwo):
pass
#__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
print(SubClass.__bases__)
>(<class '__main__.ParentOne'>, <class '__main__.ParentTwo'>)
MRO顺序
>F.MRO()
>[<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
三、覆盖(overrides)
1、子类访问父类内容
注:当子类出现了和父类名称完全一致的属性或方法
方式一
super.(当前类名称, self).你要调用的父类的属性或方法
方式二
super().你要调用的父类的属性或方法
方式三
类名称.你要调用的父类的属性或方法(self)
注:当你继承一个现有的类,并且你覆盖了父类的init方法时,必须在初始化方法的第一行调用父类的初始化方法,并传入父类所需的参数
练习:实现一个可以现在元素类型的容器(字典、列表、元组、集合、字符串)
class MyList(list):
def __init__(self, element_type):
# 调用父类初始化方法来完成基本的初始化
super().__init__()
self.element_type = element_type
def append(self, object):
"""
:param object: 是要存储的元素
:return: 无
"""
if type(object) == self.element_type:
super(MyList, self).append(object)
else:
print(f"sorry, you element type not is {self.element_type}")
m = MyList(int)
m.append(1)
print(m[0])
m.append("11")
四、封装
1、类成员-属性
1. 实例变量(字段)
2. 类变量(静态变量)
每个实例都应该拥有的变量. 比如.在上述事例中电影的的名字,电影的类型, 电影的语言,电影的时长,都是实例变量而类变量就是这一类事物统一拥有的变量,比如上述事例中可以说这些个电影都可以是3D(规定下),类变量最好是用类名来访问
方式一:
class Movie:
film_format="3D"
def __init__(self,genre,language,name,time):
self.genre=genre
self.language=language
self.name=name
self.time=time
def show(self):
print("%s在中国上映" %self.name)
M1 = Movie("剧情","国语","少林足球","2H")
M2 = Movie("喜剧","国语","大话西游","2H")
Movie.film_format="2D" #把类变量中的值改了,都跟着变了.
print(M1.film_format) #2D
print(M2.film_format) #2D
方式二:
class Movie:
film_format="3D"
def __init__(self,genre,language,name,time):
self.genre=genre
self.language=language
self.name=name
self.time=time
def show(self):
print("%s在中国上映" %self.name)
M1 = Movie("剧情","国语","少林足球","2H")
M2 = Movie("喜剧","国语","大话西游","2H")
M1.film_format="2D"
print(M1.film_format) #2D
print(M2.film_format) #3D
2、类成员-方法
(1)成员方法 - 对象直接可以访问的方法
class Computer:
# 成员方法
def play(self):
print("play game!")
computer = Computer()
computer.play()
(2)静态方法
不需要给方法传递self;即当出现一个方法不需要用到成员变量时[@staticmethod]
class Person:
def __init__(self):
pass
# 实例方法需要传递类的对象self
def think(self):
print("人能思考")
@staticmethod
def calculate(a, b):
return a + b
# 类名可以访问
person = Person.calculate(1, 2)
print(person)
> 3
(3)类方法
当方法需要传递类名时,需要类方法[@classmethod]
class Person:
def __init__(self):
pass
# cls表示类
@classmethod
def clsMethod(cls):
# 可以动态创建对象
person = cls()
print("我是一个类方法", person)
# 类方法默认第一个参数接受的是类名
Person.clsMethod()
> 我是一个类方法 <__main__.Person object at 0x00000241E7A17358>
3、类成员-属性
应用场景: 我们⼀般保存数据的时候, 不会保存⼀个⼈的年龄. 因为随着时间的推移. 每个人的年年龄都时刻在改变着. 那如何保存更更加完美呢? 很简单. 保存出生年年月日. 然后用程序来 计算,你当前的年龄实时的那这个时候就需要进行行相应的计算了了. 而计算属于⼀个功能. 当然要写方法里了. 但是对于年年龄这个属性而言. 他应该是一个数值. 而不是动作. 所以python 就提供了这样⼀种机制. 通过方法来描述⼀个属性.
注意:
- 方法参数只能有⼀个self
- 方法上方要写@property
- 调用的时候, 我们不需要写括号. 直接当成属性变量量来用就可
- 这种套路只能取值. 不能设置值
class Person:
def __init__(self, name, num, gender, birthday):
self.name = name
self.num = num
self.gender = gender
self.birthday = birthday
# 表示当前方法是一个属性.方法的返回值就是这个属性的值
@property
def age(self):
# 方法只能有一个参数self;可以是计算结果,必须有返回值
return 10 * 9
person = Person("alex", "10086", "不详", "1989-01-02")
print(person.age)
> 90
4、私有
(1)私有变量
私有的内容不能直接访问,但是如果对方开辟了外界访问的通道(公共方法),那可以通过这个公共的方法来获取到私有的内容,这样做的好处是. 外界只能看, 但是改不了.
class Tiger:
def __init__(self, name,figure, money):
self.name = name
self.figure = figure
self.__money = money
def buy(self):
print(f"我有{self.__money}这么多钱")
tiger = Tiger("永康", "正直", "1亿")
tiger.buy()
> 我有1亿这么多钱
(2)私有方法
class Tiger:
def __init__(self,name,figure,style,qingfu,money,fangchan):
self.name = name
self.figure = figure
self.style = style
self.__qingfu = qingfu
self.__money = money
self.__fangchan=fangchan
def buy(self):
print("我有%s这么多钱" % self.__money) #我有10亿这么多钱
self.__sel()
def __sel(self):
print("我要卖掉%s套房" % self.__fangchan)
T1=Tiger("呵呵","正直","廉洁","潘潘","10亿","10")
T1.buy()
>我有10亿这么多钱
>我要卖掉10套房
五、单例模式
import threading
# 多线程情况下需要加锁,因为它们共享一个内存
class Singleton(object):
_instance = None
_lock = threading.RLock()
# cls是当前正在实例化的类;初始化一块内存
def __new__(cls, *args, **kwargs):
# 如果已创建实例,直接返回即可
if cls._instance:
return cls._instance
# 加锁
with cls._lock:
if not cls._instance:
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance