python高级编程之面向对象高级编程 您所在的位置:网站首页 pythonzfill函数 python高级编程之面向对象高级编程

python高级编程之面向对象高级编程

2023-03-13 19:10| 来源: 网络整理| 查看: 265

1 面向对象编程

面向对象这节比较简单,就稍微总结几个特殊的点。

特殊方法__init__前后分别有两个下划线,__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。 有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去。 如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。 需要注意的是,在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__、__score__这样的变量名。 多态:当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,这就是多态。 “鸭子特性”:对于静态语言(例如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run()方法。 对于Python这样的动态语言来说,则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了。 2 面向对象高级编程 2.1 获取对象信息 2.1.1 type()

判断对象类型,使用type()函数:

>>> type(123) >>> type('str') >>> type(None)

如果要判断一个对象是否是函数可以使用types模块中定义的常量:

>>> import types >>> def fn(): ... pass ... >>> type(fn)==types.FunctionType True >>> type(abs)==types.BuiltinFunctionType True >>> type(lambda x: x)==types.LambdaType True >>> type((x for x in range(10)))==types.GeneratorType True

对于class的继承关系来说,使用type()就很不方便。我们要判断class的类型,可以使用isinstance()函数。

2.1.2 dir()

如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:

>>> dir('ABC') ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']

类似__xxx__的属性和方法在Python中都是有特殊用途的,比如__len__方法返回长度。在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法,所以,下面的代码是等价的:

>>> len('ABC') 3 >>> 'ABC'.__len__() 3

仅仅把属性和方法列出来是不够的,配合getattr()、setattr()以及hasattr(),我们可以直接操作一个对象的状态:

>>> class MyObject(object): ... def __init__(self): ... self.x = 9 ... def power(self): ... return self.x * self.x ... >>> obj = MyObject()

紧接着,可以测试该对象的属性:

>>> hasattr(obj, 'x') # 有属性'x'吗? True >>> obj.x 9 >>> hasattr(obj, 'y') # 有属性'y'吗? False >>> setattr(obj, 'y', 19) # 设置一个属性'y' >>> hasattr(obj, 'y') # 有属性'y'吗? True >>> getattr(obj, 'y') # 获取属性'y' 19 >>> obj.y # 获取属性'y' 19

如果试图获取不存在的属性,会抛出AttributeError的错误:

>>> getattr(obj, 'z') # 获取属性'z' Traceback (most recent call last): File "", line 1, in AttributeError: 'MyObject' object has no attribute 'z'

可以传入一个default参数,如果属性不存在,就返回默认值:

>>> getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404 404

也可以获得对象的方法:

>>> hasattr(obj, 'power') # 有属性'power'吗? True >>> getattr(obj, 'power') # 获取属性'power' >>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn >>> fn # fn指向obj.power >>> fn() # 调用fn()与调用obj.power()是一样的 81 2.2 使用__slots__

如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加name和age属性。 为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

class Student(object): __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

然后,我们试试:

>>> s = Student() # 创建新的实例 >>> s.name = 'Michael' # 绑定属性'name' >>> s.age = 25 # 绑定属性'age' >>> s.score = 99 # 绑定属性'score' Traceback (most recent call last): File "", line 1, in AttributeError: 'Student' object has no attribute 'score' 2.3 使用@property

在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是没办法检查参数,导致可以随便给属性赋值。 对于类的方法,装饰器一样起作用。Python内置的@property装饰器就是负责把一个方法变成属性调用的:

class Student(object): @property def score(self): return self._score @score.setter def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value

@property的使用方法是:把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作。

>>> s = Student() >>> s.score = 60 # OK,实际转化为s.set_score(60) >>> s.score # OK,实际转化为s.get_score() 60 >>> s.score = 9999 Traceback (most recent call last): ... ValueError: score must between 0 ~ 100!

注意到这个神奇的@property,我们在对实例属性操作的时候,就知道该属性很可能不是直接暴露的,而是通过getter和setter方法来实现的。 还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性。

2.4 多重继承

在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich继承自Bird。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich除了继承自Bird外,再同时继承Runnable。Python允许使用多重继承,这种设计通常称之为MixIn。 MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。

2.5 定制类

后面用到实际例子时再补充。

2.6 使用枚举类

当我们需要定义常量时,一个办法是用大写变量通过整数来定义,例如月份:

JAN = 1 FEB = 2 MAR = 3 ... NOV = 11 DEC = 12

好处是简单,缺点是类型是int,并且仍然是变量。 更好的方法是为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了Enum类来实现这个功能:

from enum import Enum Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

这样我们就获得了Month类型的枚举类,可以直接使用Month.Jan来引用一个常量,或者枚举它的所有成员:

for name, member in Month.__members__.items(): print(name, '=>', member, ',', member.value)

value属性则是自动赋给成员的int常量,默认从1开始计数。 如果需要更精确地控制枚举类型,可以从Enum派生出自定义类:

from enum import Enum, unique @unique class Weekday(Enum): Sun = 0 # Sun的value被设定为0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6

@unique装饰器可以帮助我们检查保证没有重复值。

2.7 使用元类

type()函数可以查看一个类型或变量的类型,例如Hello是一个class,它的类型就是type,而h是一个实例,它的类型就是class Hello。type()函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义。 我们说class的定义是运行时动态创建的,而创建class的方法就是使用type()函数。通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class:

>>> def fn(self, name='world'): # 先定义函数 ... print('Hello, %s.' % name) ... >>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class >>> h = Hello() >>> h.hello() Hello, world. >>> print(type(Hello)) >>> print(type(h))

要创建一个class对象,type()函数依次传入3个参数:

class的名称; 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法; class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。 2.7.1 metaclass

除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。metaclass,直译为元类,简单的解释就是:当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。 但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。连接起来就是:先定义metaclass,就可以创建类,最后创建实例。 所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。 先看一个简单的例子,这个metaclass可以给我们自定义的MyList增加一个add方法。定义ListMetaclass,按照默认习惯,metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass:

# metaclass是类的模板,所以必须从`type`类型派生: class ListMetaclass(type): def __new__(cls, name, bases, attrs): attrs['add'] = lambda self, value: self.append(value) return type.__new__(cls, name, bases, attrs)

有了ListMetaclass,我们在定义类的时候还要指示使用ListMetaclass来定制类,传入关键字参数metaclass:

class MyList(list, metaclass=ListMetaclass): pass

当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.__new__()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。__new__()方法接收到的参数依次是:

当前准备创建的类的对象; 类的名字; 类继承的父类集合; 类的方法集合。 测试一下MyList是否可以调用add()方法: >>> L = MyList() >>> L.add(1) >> L [1]

而普通的list没有add()方法:

>>> L2 = list() >>> L2.add(1) Traceback (most recent call last): File "", line 1, in AttributeError: 'list' object has no attribute 'add'

使用元类这块比较复杂,等楼主后面涉及到这块的实际例子再来补充这块。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有