python基础(面向对象(类)、新式类与旧式类)

本文详细介绍了Python中的面向对象编程,包括类的定义、对象的组成、类与对象的关系,以及如何创建自定义类。文章还讨论了新式类与旧式类的区别,并提供了实例解释self参数的作用。最后,作者分享了一些面向对象设计的原则和思考,以及面向对象与面向过程编程的对比。

1.类到底是什么

提到类,将其用作类型的同义词。从很多方面来说,这正是类的定义——一种对象。每个对象都属于特定的类,并被称为该类的实例。

类:类表示一组(或一类)对象,而每个对象都属于特定的类。类的主要任务是定义其实例将包含的方法。

对象:对象由属性和方法组成。属性不过是属于对象的变量,而方法是存储在属性中的函数。相比于其他函数,(关联的)方法有一个不同之处,那就是它总是将其所属的对象作为第一个参数,而这个参数通常被命名为self。


例如,如果你在窗外看到一只鸟,这只鸟就是“鸟类”的一个实例。鸟类是一个非常通用(抽象)的类,它有多个子类:你看到的那只鸟可能属于子类“云雀”。你可将“鸟类”视为由所有鸟组成的集合,而“云雀”是其一个子集。一个类的对象为另一个类的对象的子集时,前者就是后者的子类。因此“云雀”为“鸟类”的子类,而“鸟类”为“云雀”的超类。

在英语日常交谈中,使用复数来表示类,如birds(鸟类)和larks(云雀)。在Python中,约定使用单数并将首字母大写,如Bird和Lark。

通过这样的陈述,子类和超类就很容易理解。但在面向对象编程中,子类关系意味深长,因为类是由其支持的方法定义的。类的所有实例都有该类的所有方法,因此子类的所有实例都有超类的所有方法。有鉴于此,要定义子类,只需定义多出来的方法(还可能重写一些既有的方法)。
例如,Bird类可能提供方法fly,而Penguin类(Bird的一个子类)可能新增方法eat_fish。创建Penguin类时,你还可能想重写超类的方法,即方法fly。鉴于企鹅不能飞,因此在Penguin的实例中,方法fly应什么都不做或引发异常。

 

2.创建自定义类
创建自定义类,下面是一个简单的示例:
__metaclass__ = type # 如果你使用的是Python 2,请包含这行代码
class Person: 
    def set_name(self, name): 
        self.name = name 
    def get_name(self): 
        return self.name 
    def greet(self): 
        print("Hello, world! I'm {}.".format(self.name))

旧式类和新式类是有差别的。现在实在没有理由再使用旧式类了,但在Python 3之前,默认创建的是旧式类。在较旧的Python版本中,要创建新式类,应在脚本或模块开头放置赋值语句__metaclass__ = type,但我不会在每个示例中都显式地包含这条语句。当然,还有其他解决方案,如从新式类(如object)派生出子类。如果你使用的是Python 3,就无需考虑这一点,因为根本没有旧式类了。

 

新式类与旧式类区别:

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

新式类:以object为基类的类,推荐使用

经典类:不以object为基类的类,不推荐

在python3.x中定义类时,如果没有指定父类,会默认使用object作为该类的基类--python3.x中定义的类都是新式类

在python2.x中定义类时,如果没有指定父类,则不会以object作为基类

新式类和经典类在多继承时,如果没有指定父类,则不会以object作为基类

新式类和经典类在多继承时--会影响到方法的搜索顺序

为了保证编写的代码能够同时在python2.x和python3.x运行!今后在定义类时,如果没有父类,建议统一继承自object,例如:

class 类名(object):

      pass

上述示例包含三个方法定义,它们类似于函数定义,但位于class语句内。Person当然是类的名称。class语句创建独立的命名空间,用于在其中定义函数。一切看起来都挺好,但你可能想知道参数self是什么。它指向对象本身。那么是哪个对象呢?下面通过创建两个实例来说明这一点。
>>> foo = Person() 
>>> bar = Person() 
>>> foo.set_name('Luke Skywalker') 
>>> bar.set_name('Anakin Skywalker') 
>>> foo.greet() 
Hello, world! I'm Luke Skywalker. 
>>> bar.greet() 
Hello, world! I'm Anakin Skywalker. 
这个示例澄清了self是什么。对foo调用set_name和greet时,foo都会作为第一个参数自动传递给它们。我将这个参数命名为self,这非常贴切。实际上,可以随便给这个参数命名,但鉴于它总是指向对象本身,因此习惯上将其命名为self。
显然,self很有用,甚至必不可少。如果没有它,所有的方法都无法访问对象本身——要操作的属性所属的对象。与以前一样,也可以从外部访问这些属性。
>>> foo.name 
'Luke Skywalker' 
>>> bar.name = 'Yoda' 
>>> bar.greet() 
Hello, world! I'm Yoda. 

如果foo是一个Person实例,可将foo.greet()视为Person.greet(foo)的简写,但后者的多态性更低。

 

3.属性、函数和方法
实际上,方法和函数的区别表现在前一节提到的参数self上。方法(更准确地说是关联的方法)将其第一个参数关联到它所属的实例,因此无需提供这个参数。无疑可以将属性关联到一个普通函数,但这样就没有特殊的self参数了。
>>> class Class: 
...       def method(self): 
...           print('I have a self!') 
... 
>>> def function(): 
...        print("I don't...") 
... 
>>> instance = Class() 
>>> instance.method()

I have a self! 
>>> instance.method = function 
>>> instance.method()

I don't... 
请注意,有没有参数self并不取决于是否以刚才使用的方式(如instance.method)调用方法。

实际上,完全可以让另一个变量指向同一个方法。
>>> class Bird: 
...        song = 'Squaawk!' 
...        def sing(self): 
...            print(self.song) 6 
... 
>>> bird = Bird() 
>>> bird.sing() 
Squaawk! 
>>> birdsong = bird.sing 
>>> birdsong() 
Squaawk! 
虽然最后一个方法调用看起来很像函数调用,但变量birdsong指向的是关联的方法bird.sing,这意味着它也能够访问参数self(即它也被关联到类的实例)。

 

关于面向对象设计的一些思考
 将相关的东西放在一起。如果一个函数操作一个全局变量,最好将它们作为一个类的属性和方法。

  •  不要让对象之间过于亲密。方法应只关心其所属实例的属性,对于其他实例的状态,让它们自己去管理就好了。
  •  慎用继承,尤其是多重继承。继承有时很有用,但在有些情况下可能带来不必要的复杂性。要正确地使用多重继承很难,要排除其中的bug更难。
  •  保持简单。让方法短小紧凑。一般而言,应确保大多数方法都能在30秒内读完并理解。对于其余的方法,尽可能将其篇幅控制在一页或一屏内。

确定需要哪些类以及这些类应包含哪些方法时,尝试像下面这样做。
(1) 将有关问题的描述(程序需要做什么)记录下来,并给所有的名词、动词和形容词加上标记。
(2) 在名词中找出可能的类。
(3) 在动词中找出可能的方法。
(4) 在形容词中找出可能的属性。
(5) 将找出的方法和属性分配给各个类。
有了面向对象模型的草图后,还需考虑类和对象之间的关系(如继承或协作)以及它们的职责。为进一步改进模型,可像下面这样做。
(1) 记录(或设想)一系列用例,即使用程序的场景,并尽力确保这些用例涵盖了所有的功能。
(2) 透彻而仔细地考虑每个场景,确保模型包含了所需的一切。如果有遗漏,就加上;如果有不太对的地方,就修改。不断地重复这个过程,直到对模型满意为止。
有了你认为行之有效的模型后,就可以着手编写程序了。

 

面向过程与面向对象比较

面向过程

全局变量1
全局变量2
全局变量3
...

def 函数1():
    pass


def 函数2():
    pass


def 函数3():
    pass


def 函数4():
    pass


def 函数5():
    pass

面向对象

class 类(object):
    属性1
    属性2

    def 方法1(self):
        pass

    def 方法2(self):
        pass

class 类2(object):
    属性3
    def 方法3(self):
        pass

    def 方法4(self):
        pass

    def 方法5(self):
        pass
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值