Python-类-创新互联

类 (class) 0. 参考:

B站黑马程序员Python

成都创新互联是一家专注于网站制作、网站设计与策划设计,婺源网站建设哪家好?成都创新互联做网站,专注于网站建设10余年,网设计领域的专业建站公司;建站业务涵盖:婺源等地区。婺源做网站价格咨询:028-86922220
1. 面向过程和面向对象的区别:

面向过程 可以简单理解为 函数式编程,将某些独立的功能封装到函数中,然后根据步骤有顺序地调用相应的函数,最终实现整个目的。

面向对象 就是根据最终的目的来进行职责划分,根据职责来设计不同的类,在类中封装不同的属性和方法,通过类生成不同的对象,再有序地调用相应的属性或方法来实现最终目的。

2. 类的命名方式:

类名要满足大驼峰命名法,每个单词的首字母大写,单词之间没有下划线,比如NewStudent

3. 查看类的属性和方法:

dir(类名)类名.__dict__help(类名)

4.__init__初始化方法:

专门用来定义一个类具有哪些属性。

创建对象时会自动调用__init__初始化函数。

格式:self.属性 = 形参

"""定义一个 Animal类,属性包括名字和年龄。"""
class Animal:
    def __init__(self, new_name, age):
        self.name = new_name
        self.age = age
5. 封装:

根据实际需要,将属性和方法封装在类中。将类的实例化来创建对象,然后让对象去访问属性或调用方法。

举例:创建Solider类和Gun类,并让Solider用Gun进行开火。

class Gun:
    def __init__(self, model):
        self.model = model
        self.bullet_count = 0
    
    def add_bullet(self, count):
        self.bullet_count += count
    
    def shoot(self):
        if self.bullet_count<= 0:
            print("[%s] No bullet..." % self.model)
            return
        self.bullet_count -= 1
        print("[%s] shooting... [%d]" % (self.model, self.bullet_count))


class Solider:
    def __init__(self, name):
        self.name = name
        self.gun = None

    def fire(self):
        if self.gun is None:
            print("[%s] has No Gun" % self.name)
            return
        print("Go... [%s]" % self.name)
        ## 将 self.gun 对象对应的方法(add_bullet和shoot)封装到类中
        self.gun.add_bullet(5)
        self.gun.shoot()
        


if __name__ == "__main__":
    ak47 = Gun("AK47")
    xsd = Solider("XSD")
    
    xsd.gun = ak47 ## 一个对象的属性(xsd.gun)可以是另一个类创建的对象(ak47)
    xsd.fire()
6. 私有属性和私有方法:

1. 私有属性和私有方法都不可以在外部被直接调用。
2. 尽管如此,如果要强行访问私有属性和私有方法,也是可以的。具体的方法就是:实例._类名__私有属性名或私有方法名

格式:__属性名__方法名
举例:

class Women(object):
    def __init__(self, name):
        self.name = name
        self.__age = 20 ## 私有属性,在外部不能直接访问

    def __secret(self):
        ## 在对象的方法内部可以访问对象的私有属性
        print("[%s] age is [%d]" % (self.name, self.__age))

if __name__ == "__main__":
    xf = Women("XF")
    ## 访问私有属性、调用私有方法
    print(xf._Women__age)
    xf._Women__secret()
7. 继承:

子类 拥有 父类 的所有属性和方法。

格式:class 类名(父类名)

继承 具有传递性,即:子类 拥有 父类 以及 父类的父类 的所有属性和方法。

当 父类 的方法不能满足子类的需求时,需要对方法进行重写:

1. 覆盖父类的方法:当父类的方法完全不能满足子类的需求时,可以在子类中定义一个与父类方法同名的方法。

2. 扩展父类的方法:当父类的方法部分可以满足子类的需求时,可以对父类方法进行扩展,具体步骤:
2.1 在子类中定义一个与父类方法同名的方法;
2.2 在该子类方法中,使用super().父类方法来调用父类方法;
2.3 代码其他位置可以根据子类的需求来编写子类特有的的代码;

重写父类方法的举例:

class Animal:
    def bark(self):
        print("Animal Bark")

class Dog(Animal):
    def fly(self):
        print("Dog Fly")

    def bark(self):
        print("Dog Bark")


if __name__ == "__main__":
    xx = Dog()
    xx.fly()
    xx.bark()

扩展父类方法的举例:

class Animal:
    def bark(self):
        print("Animal Bark")

class Dog(Animal):
    def bark(self):
        print("Dog GaGaGa")
        super().bark()
        print("!@#$%^&*")


if __name__ == "__main__":
    xx = Dog()
    xx.bark()
8. 继承中的私有属性和私有方法:

子类对象 不能在自己方法内部 直接访问 父类的私有属性或私有方法,但是可以 通过父类的共有方法 间接访问到 私有属性或私有方法。

举例:子类对象不能在自己的方法内部直接访问父类的私有属性或私有方法。

class A:
    def __init__(self):
        self.num1 = 100
        self.__num2 = 200

    def __test(self):
        print("私有方法 %d %d" % (self.num1, self.__num2)) ## 在自己的方法内部可以访问自己的私有属性或私有方法

class B(A):
    def demo(self):
        ## 在子类的方法中不能访问父类的私有属性
        print("父类私有属性 %d" % self.__num2)
        ## 在子类的方法中不能调用父类的私有方法
        self.__test()


if __name__ == "__main__":
    b = B()
    b.demo()

举例:子类对象可以通过父类中的共有属性或共有方法来间接访问父类的私有属性或私有方法。

class A:
    def __init__(self):
        self.num1 = 100
        self.__num2 = 200

    def __test(self):
        print("父类的私有方法 %d %d" % (self.num1, self.__num2))

    def test(self):
        print("父类的私有属性 %d" % self.__num2)
        ## 父类的私有方法
        self.__test()

class B(A):
    def demo(self):
        ## 通过父类的共有属性或公有方法来访问父类的私有属性或私有方法
        self.test()


if __name__ == "__main__":
    b = B()
    b.demo()
9. 多继承:

子类可以继承多个父类,并且继承所有父类的属性和方法。
格式:class 子类名(父类名1, 父类名2, ...)

class A:
    def test(self):
        print("A --- test")

class B:
    def demo(self):
        print("B --- demo")

class C(A, B):
    ## 多继承可以让子类同时具有多个父类的属性和方法
    pass


if __name__ == "__main__":
    c = C()
    c.test()
    c.demo()

多继承的注意事项:
如果多个父类之间存在同名的属性或方法时,尽量不要用多继承。

多继承中的MRO (method resolution order),主要用于多继承时判断 方法、属性的调用路径。

class A:
    def test(self):
        print("A --- test")
    
    def demo(self):
        print("A --- demo")

class B:
    def test(self):
        print("B --- test")
    
    def demo(self):
        print("B --- demo")

class C(B, A):
    ## 多继承可以让子类同时具有多个父类的属性和方法
    pass


if __name__ == "__main__":
    print(C.__mro__)
	## 结果:
	## (,,,)
10.object基类

object是所有对象的基类,提供一些内置的属性和方法,可以使用dir()函数查看。

Python3 中默认以object为基类。

11. 多态:

不同的子类对象调用相同的父类方法,产生不同的结果。

以继承和重写父类方法为前提。因为不同的对象都有相同的方法名,所以可以根据相同的方法名而使不同的对象产生不同的结果。

## 多态
class Animal(object):
    def __init__(self, name):
        self.name = name
    
    def game(self):
        print("Animal Play %s" % self.name)

class Dog(Animal):
    def game(self):
        print("Dog Play %s" % self.name)

class Person:
    def __init__(self, name):
        self.name = name
    
    def game_with(self, dog):
        print("%s play with %s" % (self.name, dog.name))
        dog.game() 
        ##因为 Animal 和 Dog 中都有 game方法,所以这里的dog.game()既可以是Animal中的game(),也可以是Dog中的game()


if __name__ == "__main__":
    ## 让不同的子类对象调用相同的父类方法产生不同的结果:
    ## ww 和 aa 是不同的子类对象,对象xm通过调用相同的方法(game_with())来产生不同的结果
    ww = Animal("WW")
    aa = Dog("AA")
    
    xm = Person("XM")
    
    xm.game_with(ww)
    xm.game_with(aa)   
12. 类属性和类方法:

Python中一切皆对象;
1.class AAA:定义类对象;
2. 类对象可以拥有自己的属性(类属性)和方法(类方法);
3. 可以通过类名.的方式来访问类属性或调用类方法;
4. 程序运行时,类对象也会加载到内存中,但是只有一份。通过类对象可以创建出多个实例;

12.1 类属性:

类属性就是类对象中定义的属性。

举例:

## 使用类创建了多少个对象
class Tool(object):
    count = 0
    def __init__(self, name):
        self.name = name
        Tool.count += 1


if __name__ == "__main__":
    ## 创建工具对象
    t1 = Tool("斧头")
    t2 = Tool("锤头")
    t3 = Tool("勺子")

    ## 输出对象个数
    print(Tool.count)

针对上述脚本有一个问题:为什么在用Tool创建实例的时候,第三行中的count=0不会被重复调用?
我的理解是:运行程序,第一次创建实例时需要把class Tool加载到内存中,因此在加载过程中会从头开始(包括count=0__init__)。因为类对象在内存中只需要有一份,所以后续再创建实例时,不需要再从头加载,只需要运行__init__初始化方法即可,也就是说不需要再运行count=0,直接运行__init__方法。因为没有再运行count=0,所以此时的count对应的数值是上一次创建实例时生成的结果。所以count的数值会被累加起来。

其实针对上面有一点需要理解,就是count += 1会改变count的内存地址,如下所示:
在这里插入图片描述

对于n += 1n的地址发生改变的理解:
可以先理解Python中对变量a = 2的理解,就是说a存储的不是整数2而是整数2对应的内存地址。当2就变成3的时候,相应的内存地址也就发生了变化,所以a对应的内存地址也就发生了变化。
由此理解n += 1,就是说首先创建一个不可变对象 (整数n=初始值),之后再把n + 1赋给n(内存地址改变) ,然后回收之前的n

Python中可变对象和不可变对象:

不可变对象:int,float,str,tuple
可变对象:字典,集合,列表,都属于可变对象,说其可变,是指其内存中的值可变。
举例:
(可变对象,变量的内存地址不变)
在这里插入图片描述
(不可变对象,变量的内存地址改变)
在这里插入图片描述

12.2 类方法@classmethod

类方法就是针对类定义的方法,类方法的内部可以直接访问类属性或调用其他的类方法。

格式:
@classmethod
def 类方法名(cls):

1. 类方法需要@classmethod修饰器来标识,告诉解释器这是一个类方法;
2. 通过类名.的方式来调用类方法;
3. 在方法内部可以通过cls.来访问类的属性,也可以通过cls.来调用其他的类方法;

举例:

## 使用类创建了多少个对象
class Tool(object):
    count = 0

    @classmethod
    def show_tool_count(cls):
        print("工具对象的数量 %d" % cls.count) ## 访问当前类的类属性

    def __init__(self, name):
        self.name = name
        Tool.count += 1

if __name__ == "__main__":
    t1 = Tool("斧头")
    t2 = Tool("锤头")
    t3 = Tool("勺子")
    
    ## 调用 类方法
    Tool.show_tool_count()  
13. 静态方法@staticmethod

什么都不需要的时候(既不需要访问类属性或实例属性,也不需要调用类方法或实例方法),可以把这个方法封装成静态方法。

格式:
@staticmethod
def 静态方法名():

通过类名.的方式来调用静态方法。

举例:

class Animal(object):
    @staticmethod
    def fly():
        print("Flying ...")

Animal.fly()
14. 案例:
## 创建游戏类
class Game(object):
    top_score = 0 ## 类属性

    def __init__(self, player_name):
        self.player_name = player_name ## 实例属性

    @staticmethod
    def show_help(): ## 静态方法不需要访问任何属性
        print("Help Information")
    
    @classmethod
    def show_top_score(cls):
        print("Top Score in History : %d" % cls.top_score) ## 类方法中访问类属性:cls.类属性名
    
    def start_game(self):
        print("%s Start gaming..." % self.player_name) ## 实例方法中访问实例属性
        print("Top Score is %d" % Game.top_score) ## 实例方法中访问类属性:类名.类属性名


def main():
    ## 1. 查看帮助信息
    Game.show_help()

    ## 2. 显示历史记录
    Game.show_top_score()

    ## 3. 显示玩家信息
    xm = Game("Ming")
    xm.start_game()


if __name__ == "__main__":
    main()

小结:
1. 实例方法 —— 方法内部需要访问实例属性(方法内部也可以用类名.类属性的方法来访问类属性)start_game()
2. 类方法 —— 方法内部只需要访问类属性show_top_score()
3. 静态方法 —— 方法内部不需要任何属性或方法 (show_help());

15. 单例:

单例设计模式:
用类创建对象,在系统中只有唯一一个实例。就是说,每次运行类名()创建实例时,内存地址是相同的。
实现方式是重写__new__方法。

15.1 内置方法__new__

类名()创建实例时,Python解释器首先会调用__new__方法为对象分配内存空间。
__new__是一个由object基类提供的内置静态方法,主要作用是:为对象分配内存空间;返回对象的引用。Python解释器获得对象的引用后,将引用作为第一个参数传递个__init__方法。

有一个问题就是说:为啥__new__(cls, *args, **kwargs)是一个静态方法。它不是有一个cls参数吗?
【参考 :https://segmentfault.com/q/1010000016949076,尽管我暂时还是看不懂】

15.2 重写__new__
## __new__ 方法的重写
class MusicPlayer(object):
    def __new__(cls):
        ## 创建对象是,__new__方法会被自动调用
        print("创建对象,分配空间")
        
        ## 分配空间
        instance = super().__new__(cls) ## __new__是静态方法,需要手动传递cls参数
        
        ## 返回对象的引用
        return instance
    
    def __init__(self):
        print("播放器初始化")
    

if __name__ == "__main__":
    player = MusicPlayer()
    print(player)

这里重写的__new__方法并不能创建单例对象,如下图所示内存地址并不相同:
在这里插入图片描述

15.3 如何实现 创建单例对象:

在这里插入图片描述

创建单例对象的话,不仅仅需要重写__new__方法,而且还需要对内存地址是否存在进行判断。
其实上面代码中的类属性instance = None就是用来判断是否存在内存地址的。第一次创建实例时,instance通过父类中的__new__方法被赋予了内存地址,因此当后续在创建实例时,instance就不再是None,也就不会再运行父类中的__new__方法,而是直接返回类属性instance,由此变实现了后续创建多个实例的时候,内存地址不会变化。

15.4 如何实现 只执行一次初始化动作:

在这里插入图片描述

上面代码通过设置初始化标签init_flag来判断是否执行过初始化动作,如果执行过,后续运行__init__方法的时候直接return即可。

16. 内置方法__del__

作用:当一个对象被从内存中销毁之前,会被自动调用;
如果希望在对象被销毁之前再做一些事情,可以考虑__del__方法;

Python 3.9.12 中好像没有__del__内置方法,但是用del 对象名倒是会调用__del__这个方法(如下结果所示:)。
在这里插入图片描述
上面代码从左到右可以发现,第一次创建实例时,实例不会自动销毁。而当调用del方法时,会首先将第一次创建的实例删掉,然后再第二次创建实例。而当第三次创建实例时,因为第二次创建的实例已经被销毁,因此第三次创建实例的时候不会先运行del方法。

生命周期:一个对象调用类名()创建实例,声明周期开始;一旦调用__del__方法,生命周期结束;在对象的生命周期内,可以访问对象属性或者调用方法。

17. 内置方法__str__

返回对象的描述信息,print函数输出使用;
需要注意的是__str__必须返回一个字符串;

使用print输出对象时,默认情况下会输出该对象是由哪一个类创建的以及其在内存中的地址(如下所示:)
在这里插入图片描述

当不想输出上述默认哪些信息时,可以用__str__来输出特定信息(如下所示):
在这里插入图片描述

18. 关于机器学习中遇到的super(XXX, self).__init__()的理解:

参考:https://blog.csdn.net/dongjinkun/article/details/114575998 (这篇文章写得很好)

简言之:super(XXX, self).__init__()就是用父类中的初始化方法来对子类的属性进行初始化,如下所示(以这篇文章中的例子为例):在这里插入图片描述

定义了两个类:PersonStu,其中子类Stu继承了父类Personsuper(Stu, self).__init__(name, gender)调用父类Person中的初始化方法对子类Stu中的namegender属性进行初始化,所以子类中的namegender会被大写。而school不作处理,所以不会被大写。

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


当前题目:Python-类-创新互联
分享链接:http://scyanting.com/article/dhjiee.html