Python-类和对象

类是一个模板,用于定义一组相关的数据和方法。类中可以包含数据成员(如字符串、数字、列表等)和方法成员(如构造函数、析构函数、方法等)。类可以被看作是一种数据结构,用于组织和管理数据。

对象是类的实例,是类的具体表现形式。每个对象都有一个类(称为类对象),并且可以被赋值给变量。对象包含类的数据成员和方法成员,并且可以执行类中定义的方法。

一、对象=属性+方法

Python中一个对象的特征称为“属性”,一个对象的行为称为“方法”。

以下代码定义了对象的特征(属性)和行为(方法),但还不是一个完整的对象,将定义的这些称为类(class)。需要使用类来创建一个真正的对象,这个对象就称为这个类的一个实例(instance),也叫实例对象(instance objects)。

真正的实例对象,需要将类实体化,如下列程序所示

     >>> # 这里先运行p11_1.py
     >>> tt = Turtle()
     >>>

注意:类名后面跟着小括号,这与调用函数是一样的,所以在Python中,类名约定用大写字母开头,函数用小写字母开头,这样更容易区分。另外赋值操作并不是必需的,但如果没有把创建好的实例对象赋值给一个变量,那这个对象就没办法使用,因为没有任何引用指向这个实例,最终会被Python的垃圾收集机制自动回收。

如果要调用对象里的方法,使用点操作符(.)即可:

     >>> tt.climb()
     我正在很努力的向前爬...
     >>> tt.bite()
     咬死你咬死你!!
     >>> tt.sleep()
     困了,睡了,晚安,Zzzz

二、面向对象编程

  1. self

对象的方法都会有一个self参数,它相当于C++的this指针,由同一个类可以生成无数对象,当一个对象的方法被调用的时候,对象会将自身的引用作为第一个参数传给该方法,那么Python就知道需要操作哪个对象的方法了。

2.Python魔法方法

Python的对象拥有一些神奇的方法,它们是面向对象的Python的一切,如果对象实现了这些方法中的某一个,那么这个方法就会在特殊的情况下被Python调用,而这一切都是自动发生的,Python的这些具有魔力的方法,总是被左右各两个下画线所包围。

一个最基本的特殊方法:_ _init_ _(),通常把_ _init_ _()方法称为构造方法,_ _init_ _()方法的魔力体现在只要实例化一个对象,这个方法就会在对象被创建时自动调用(在C++里也可以看到类似的东西,叫“构造函数”)。其实,实例化对象时是可以传入参数的,这些参数会自动传入_ _init_ _()方法中,可以通过重写这个方法来自定义对象的初始化操作。

3、公有和私有

Python中默认对象的属性和方法都是公开的,可以直接通过点操作符(.)进行访问:

为了实现类似私有变量的特征,Python内部采用了一种叫Name Mangling(名字改编)的技术,在Python中定义私有变量只需要在变量名或函数名前加上“_ _”两个下画线,那么这个函数或变量就会成为私有的了:

这样在外部将变量名“隐藏”起来了,理论上如果要访问,就需要从内部进行:

但可以在外部使用“_类名_ _变量名”,访问两个下画线开头的私有变量了:

     >>> p._Person_ _name
     '小甲鱼'

注意:Python目前的私有机制其实是伪私有,Python的类是没有权限控制的,所有变量都是可以被外部调用的。

三、继承

(Python的继承是一种面向对象编程的概念,它允许一个类(称为子类或派生类)继承另一个类(称为基类或父类)的属性和方法。子类可以重写(覆盖)从父类继承的方法,并可以添加新的属性和方法以改进其行为。这种能力使得Python的代码更加模块化和复用,因为可以在不改变原始代码的情况下扩展或修改其行为。

以下是Python继承的一些基本概念:

  1. 继承:子类(派生类)从父类(基类)继承属性和方法。这种关系称为继承。
  2. 基类:基类是一个类,它定义了父类(超类)的属性和方法。基类是其他类的父类,它们可以从基类继承属性和方法。
  3. 子类:子类是基类的一个实例,它具有从基类继承的属性和方法。子类可以覆盖(重写)从基类继承的方法,并可以添加新的属性和方法以改进其行为。
  4. 访问权限:在Python中,基类的属性和方法默认是公共的,可以从多个派生类中访问。然而,私有属性和方法只能在派生类中访问,不能从基类或其他派生类中访问。
  5. 多重继承:在某些情况下,一个子类可以继承多个基类。这种情况下,子类将获得所有从基类继承的属性和方法,这被称为多重继承。)

继承的语法:

被继承的类称为基类、父类或超类;继承者称为子类,一个子类可以继承它的父类的任何属性和方法。

需要注意的是,如果子类中定义与父类同名的方法或属性,则会自动覆盖父类对应的方法或属性:

下面写一个对鱼类进行细分的程序,有金鱼(Goldfish)、鲤鱼(Carp)、鲨鱼(Shark),还有好吃的三文鱼(Salmo)。

程序实现如下:

鲨鱼(Shark)一移动就会报错,因为Shark对象没有x属性,在Shark类中,重写了魔法方法_ _init_ _,但新的_ _init_ _方法里边没有初始化鲨鱼的x坐标和y坐标,因此调用move方法就会出错。

解决这个问题的方案:在鲨鱼类中重写_ _init_ _方法的时候先调用基类Fish的_ _init_ _方法。

下面介绍两种可以实现的技术:

• 调用未绑定的父类方法。

• 使用super函数。

1、调用未绑定的父类方法

再运行后发现鲨鱼也可以成功移动了:

     >>> # 先运行修改后的p11-2.py
     >>> shark = Shark()
     >>> shark.move()
     我的位置是: 7 9
     >>> shark.move()
     我的位置是: 6 9

需要注意的是,这个self并不是父类Fish的实例对象,而是子类Shark的实例对象,所以,这里说的未绑定是指并不需要绑定父类的实例对象,使用子类的实例对象代替即可。

2.使用super函数

super函数能够自动找到基类的方法,而且还传入了self参数:

运行后得到同样的结果:

     >>> # 先运行修改后的p11-2.py
     >>> shark = Shark()
     >>> shark.move()
     我的位置是: 6 1
     >>> shark.move()
     我的位置是: 5 1

super函数的“超级”之处在于:不需要明确给出任何基类的名字,它会自动找出所有基类以及对应的方法。由于不用给出基类的名字,这就意味着如果需要改变类继承关系,只要改变class语句里的父类即可,而不必在大量代码中去修改所有被继承的方法。

四、多重继承

Python还支持多继承,就是可以同时继承多个父类的属性和方法。多重继承的语法如下:

举个例子:

但多重继承容易导致代码混乱,所以当不确定是否必须使用多重继承的时候,请尽量避免使用它,因为有些时候会出现不可预见的BUG。

五、组合

把需要的类放进去实例化就可以了,这就叫组合:

程序实现如下:

     >>> # 先运行p11_3.py
     >>> pool = Pool(1, 10)
     >>> pool.print_num()
     水池里总共有乌龟 1 只,小鱼 10 条!

六、类、类对象和实例对象

对实例对象c的count属性进行赋值后,就相当于覆盖了类对象C的count属性,如果没有赋值覆盖,那么引用的是类对象的count属性。

需要注意的是,类中定义的属性是静态变量,也就是相当于C语言中加上static关键字声明的变量,类的属性是与类对象进行绑定,并不会依赖任何它的实例对象。这点待会儿继续讲解。

另外,如果属性的名字与方法名相同,属性会覆盖方法:

为了避免名字上的冲突,编写代码时应该遵守一些约定俗成的规矩:

• 类的定义要“少吃多餐”,不要试图在一个类里边定义出所有能想到的特性和方法,应该利用继承和组合机制来进行扩展。

• 用不同的词性命名,如属性名用名词、方法名用动词,并使用骆驼命名法。骆驼式命名法(CamelCase)又称驼峰命名法,是电脑程式编写时的一套命名规则(惯例)。正如它的名称CamelCase所表示的那样,是指混合使用大小写字母来构成变量和函数的名字,程序员为了自己的代码能更容易在同行之间交流,所以多采取统一的可读性比较好的命名方式。

七、绑定

1.绑定

Python严格要求方法需要有实例才能被调用,这种限制其实就是Python所谓的绑定概念。

但有时类可以直接被调用

但这样做会有一个问题,就是根据类实例化后的对象根本无法调用里边的函数:

经过如下改进后即可使用

class BB:
    def printBB(self):
        print("123")

bb = BB()
bb.printBB()

而错误产生实际上由于Python的绑定机制,这里自动把bb对象作为第一个参数传入,所以才会出现TypeError。

2._ _dict_ _

_ _dict_ _属性由一个字典组成,字典中仅有实例对象的属性,不显示类属性和特殊属性,键表示的是属性名,值表示属性相应的数据值。

     >>> dd.setXY(4, 5)
     >>> dd._ _dict_ _
     {'x': 4, 'y': 5}

现在实例对象dd有了两个新属性,而且这两个属性仅属于实例对象:

     >>> CC._ _dict_ _
     mappingproxy({'_ _doc_ _': None, '_ _dict_ _': <attribute '_ _dict_ _' of 'CC'
     objects>, '_ _weakref_ _': <attribute '_ _weakref_ _' of 'CC' objects>,
     'printXY': <function CC.printXY at 0x0370D2B8>,'_ _module_ _': '_ _main_ _',
     'setXY': <function CC.setXY at 0x034A1420>})

只属于dd而不属于CC的原因是:self参数,当实例对象dd去调用setXY方法的时候,它传入的第一个参数就是dd,那么self.x = 4,self.y = 5也就相当于dd.x = 4,dd.y = 5,所以在实例对象甚至类对象中,都看不到x和y,因为这两个属性是只属于实例对象dd的。

如果把类实例删除掉,实例对象dd还能调用printXY方法。

     >>> del CC
     >>> dd.printXY()
     4 5

八、一些相关的BIF

  1. issubclass(class, classinfo)

如果第一个参数(class)是第二个参数(classinfo)的一个子类,则返回True,否则返回False。

(1)一个类被认为是其自身的子类。

(2)classinfo可以是类对象组成的元组,只要class是其中任何一个候选类的子类,则返回True。

(3)在其他情况下,会抛出一个TypeError异常。

2. isinstance(object, classinfo)

如果第一个参数(object)是第二个参数(classinfo)的实例对象,则返回True,否则返回False。

(1)如果object是classinfo的子类的一个实例,也符合条件。

(2)如果第一个参数不是对象,则永远返回False。

(3)classinfo可以是类对象组成的元组,只要object是其中任何一个候选类的子类,则返回True。

(4)如果第二个参数不是类或者由类对象组成的元组,会抛出一个TypeError异常。

     >>> issubclass(B, C)
     False
     >>> b1 = B()
     >>> isinstance(b1, B)
     True
     >>> isinstance(b1, C)
     False
     >>> isinstance(b1, A)
     True
     >>> isinstance(b1, (A, B, C))
     True

Python提供了以下几个BIF用于访问对象的属性。

3. hasattr(object, name)

attr即attribute的缩写,属性的意思。第一个参数(object)是对象,第二个参数(name)是属性名(属性的字符串名字)。

4. getattr(object, name[, default])

返回对象指定的属性值,如果指定的属性不存在,则返回default(可选参数)的值;若没有设置default参数,则抛出ArttributeError异常。

5. setattr(object, name, value)

与getattr()对应,setattr()可以设置对象中指定属性的值,如果指定的属性不存在,则会新建属性并赋值。

     >>> setattr(c1, 'y', 'FishC')
     >>> getattr(c1, 'y')
     'FishC'

6. delattr(object, name)

与setattr()相反,delattr()用于删除对象中指定的属性,如果属性不存在,则抛出AttributeError异常。

7)property(fget=None, fset=None, fdel=None, doc=None)

property()是一个比较“奇葩”的BIF,它的作用是通过属性来设置属性。

程序实现如下:

property()返回一个可以设置属性的属性,当然如何设置属性还是需要人为来写代码。第一个参数是获得属性的方法名(例子中是getSize),第二个参数是设置属性的方法名(例子中是setSize),第三个参数是删除属性的方法名(例子中是delSize)。

property()可用于在大型程序,对对象的属性进行修改。

posted @ 2024-03-04 14:08  挖坑达人  阅读(653)  评论(0)    收藏  举报