Python面向对象编程

Python中的类和其他部分一样,也是动态的,类被动态创建,创建后还可以修改。

Python中变量的赋值和传递,操作的总是对象的引用。一个对象可以绑定多个名字,多个变量可以指向同一个对象。

Python的对象更加强调鸭子类型,特定情况下看起来像鸭子,就是鸭子。

命名空间和作用域

命名空间是名字到对象的映射。目前大部分命名空间在python中都是以字典的形式存在的。

命名空间在不同的时刻被创建,同时拥有不同的生命周期。built-in命名空间在python解释器启动时被创建,并且不会被删除;一个模块的全局命名空间在模块定义被读取时创建,一般也会一直存在,直到解释器退出。局部命名空间在函数被调用时创建,在离开函数时被删除。

作用域是程序中命名空间直接对应的文本区域,在该区域,可以不加前缀修饰的直接访问对应的命名空间。作用域是静态决定的,但是被动态的使用。任何时候,至少有3个嵌套的作用域。

  1. 最内部的作用域,首先被访问,包含局部变量
  2. 外围函数作用域,按从内到外的顺序,从最里层作用域开始被查找,包含非局部,但是也非全局的变量名
  3. 全局作用域,次外层作用域,包含模块的全局命名
  4. 最外层作用域,包含内建的名称

类定义在本地作用域中引入了一个新的命名空间。

global和nolocal

global语句用于指明变量存在于全局作用域,nolocal指明变量存在于外围作用域,除去global和nolocal语句,所有的引入新命名的操作都使用局部作用域。

类对象

类定义语句执行完成后,一个类对象被创建。类对象支持两类操作,属性引用和实例化

class MyClass:
    """A simple example class"""
    i = 12345

    def f(self):
        return 'hello world'

所有在类的命名空间中的名字都是有效的属性名。 MyClass.i ,MyClass.f和MyClass.__doc__都是有效的属性。
类属性分为数据属性和行为属性,数据属性是类的状态,行为是绑定到类的函数,对应着实例对象的方法。

x = MyClass()

实例化一个MyClass类型的对象,赋值到局部变量x

通过__init__初始化实例对象

def __init__(self):
    self.data = []

__init__可以带有其他参数,传入实例化操作符的参数将会被被传递到__init__中。

>>> class Complex:
... def __init__(self, realpart, imagpart):
... self.r = realpart.
.. self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
(3.0, -4.5)

实例对象

实例对象仅仅支持属性引用操作。实例属性分为数据属性和方法属性,数据属性是实例的状态,方法是绑定到实例的函数。

x = MyClass()
x.counter = 1
while x.counter < 10:
    x.counter = x.counter * 2
print(x.counter)
del x.counter

x.counter是实例的数据属性,在赋值时创建,通过del可删除。
x.f是实例对象的方法,源于MyClass中定义的函数f。MyClass.f是函数,x.f是方法。

>>> x.i
12345
>>> x.i = 54321
>>> x.i
54321

x.i也是数据属性,在不被赋值的情况下引用到类的属性MyClass.i,但是一旦赋值,则引用到属于实例自己的实例属性。

方法对象

方法是对象,所以支持赋值到变量

xf = x.f
while True:
    print(xf())

xf() 等同于x.f()

类定义中方法定义的第一个参数(惯例命名为self)指示绑定的实例对象,在方法调用时,该参数被自动被传递。
x.f()等价于MyClass.f(x)

当实例对象的非数据属性被引用,对应的类对象被查找,如果该属性名是是类的一个属性,并且是函数对象,一个方法对象被创建,该对象封装实例对象和对应的函数,当方法对象被传入参数调用,通过实例对象和老的参数列表,一个新的参数列表被生成,随后对应的函数被通过传入该新创建的参数列表调用。

一个函数如果是类的一个属性,则该函数就定义了一个类实例对象的方法,函数本身的定义不需要位于类的定义中。

# Function defined outside the class
def f1(self, x, y):
    return min(x, x+y)

class C:
    f = f1

    def g(self):
        return 'hello world'

    h = g

x.i在不被赋值的情况下引用到类的属性MyClass.i,但是一旦赋值,则引用到属于实例自己的实例属性。

在方法中对当前对象其他属性的引用,必须通过方法的第一个参数self引用,并无其他快捷方式。

和函数一样,在方法中可以直接引用全局变量,全局变量所在的作用域是方法所在的模块,通常包含方法的类被直接定义在全局作用域中,类本身不作为全局作用域。虽然很少在方法中引用全局变量,但是有时却是很有用的,比如应用全局作用域中导入的函数,模块,以及模块中的其他元素。

方法对象的属性__self__指向其绑定的实例对象,   __func__执行其对应的函数。

类变量和实例变量

类变量是被所有类的实例对象共享的变量,实例变量是唯一属于当个实例对象的变量


class Dog:

   kind = 'canine'         # class variable shared by all instances

   def __init__(self, name):
       self.name = name    # instance variable unique to each instance

>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind                  # shared by all dogs'canine'
>>> e.kind                  # shared by all dogs'canine'
>>> d.name                  # unique to d'Fido'
>>> e.name                  # unique to e'Buddy'

属性的可见性

Python不提供实例对象属性的可见性配置功能,所有的属性在python中都是公有的,使用属性时如果任务修改了被方法操作,维护的属性,则可能影响整个对象的状态,这会导致很难排查的bug,所以python中尽可能对属性的命名保持一个惯例,尽可能减少属性名的冲突。

对于私有变量,python的惯例是以一个下划线开头的变量被认为是私有变量,对象的用户不应该使用这类变量。

对于类对象的私有变量,为了避免实现子类时的命名冲突,python提供了名字重编的功能,以至少两个下划线开头,至多一个下划线结尾的类变量,python会把用到该变量的地方都改为_ClassName+变量名。

class Mapping:
    def __init__(self, iterable):
        self.items_list = []
        self.__update(iterable)

    def update(self, iterable):
        for item in iterable:
            self.items_list.append(item)

    __update = update   # private copy of original update() method

class MappingSubclass(Mapping):

    def update(self, keys, values):
        # provides new signature for update()
        # but does not break __init__()
        for item in zip(keys, values):
            self.items_list.append(item)

Mapping._Mapping__update和Mapping.update都是有效的属性, Mapping.__update却是无效的属性。

继承

继承语法:

class DerivedClassName(BaseClassName):
    <statement-1>
    .
    .

    .
    <statement-N>

基类的位置可以是任意的表达式,只要求值为一个类对象。

由于属性解析的顺序是子类-》父类,子类可以重载父类的方法,很容易实现多态。

如果在子类重写的方法中需要调用基类的方法,可以使用
BaseClassName.methodname(self, arguments)
或者
super(DerivedClassName, self).methodname(arguments)

isinstance()和issubclass()

isinstance() 用于检查实例的类型。
isinstance(obj, int)当obj.class 是int或int的子类时为真。

issubclass() 用于检查类的继承:。
issubclass(bool, int) bool是int的子类,所以返回真;issubclass(float, int) float不是int的子类所以返回假。

多重继承

多重继承语法:

class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>

简单说来,一个位于父类中的属性的搜索顺序是深度优先,从左到右,在继承树中重复的同一个类不会被搜索两次。实际上方法的解析顺序要复杂一些,是动态变化的。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值