zoukankan      html  css  js  c++  java
  • 『Python』面向对象(一)

    类和对象

    类(class)是用来描述具有相同属性(attribute)和方法(method)的对象的集合,对象(object)是类(class)的具体实例。比如学生都有名字和分数,他们有着共同的属性。这时我们就可以设计一个学生类, 用于记录学生的名字和分数,并自定义方法打印出他们的名字和方法。

    class Student(object):
        def __init__(self,name,age,score):
            self._name = name
            self._age = age
            self._score = score
    
        def show(self):
            print("{}的年龄是{}岁,分数是{}分".format(self._name,self._age,self._score))
    
    aStudent = Student("瑞雯",18,99)
    
    • 属性:类里面用于描述所有对象共同特征的变量或数据。如_name_age_score
    • 方法:类里面的函数,用来区别类外面的函数, 用来实现某些功能。如show()
    • 对象(实例):根据类创建出来的,如aStudent

    属性

    类属性

    定义在类中函数体外的属性,属于类共有属性

    class A(object):
        count = 0
        def __init__(self):
             # 类中只有通过类名.类属性访问,self.__class__返回的就是类名
            self.__class__.count += 1	# 或 A.count += 1
            
    a = A()
    b = A()
    
    # 类外既可以通过类来访问,也可通过类的实例来访问
    print(a.count)  # 2
    print(A.count)  # 2
    

    要修改类属性必须通过类名.属性的方式来修改,而不能通过实例,因为实例.属性这种方式其实是创建了同名的实例属性,屏蔽了类的属性,通过del 实例.属性操作后,会发现实例.属性还是之前的。

    实例属性

    定义在__init__()方法中,属于对象

    class B(object):
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
    a = B("易",18)
    b = B("金克斯",8)
    

    一般来说,对象的属性应该设为private,不能被外界直接访问,这里暂且不管访问权限问题。当能从外界直接访问时,必须通过对象.属性来访问,这时候属性是属于对象实例的。

    访问控制

    Python中并没有publicprotectedprivate这样的关键字,所以无法实现数据封装,只能从语法上来定义可见性,依靠程序员自觉遵守规约。

    Python中,不存在函数的重载,因为函数名和普通变量名一样,都是引用,指向一个对象,是一对一的关系,都是标识符。Python中就是通过标识符的命名来区分访问的可见性。

    • 字符开头的标识符,如:age
      这种标识符相当于public,可以通过对象.属性或者对象.方法来执行。

    • 双下划线开头结尾的标识符,如: __init__
      用户最好不要自定义这种类型的标识符,因为这通常是系统调用。

    • 单下划线开头的标识符,如:_age
      这种标识符相当于protected,不过Python中没有protected的概念,所以被视为private,但是,你可以按照public用,属于推荐private但是可以public的。

    • 双下划线开头的标识符,如:name
      相当于privatePython通过更改标识符名(改为_类名__标识符)来实现无法访问的机制。

    方法

    实例方法

    class Test(object):
        def __init__(self,name,age):
            self.__name = name
            self.__age = age
    
        def grow(self,growth):
            self.__age += growth
    

    只能被对象实例调用,第一个参数必须是self,它指向调用这个方法的实例。

    类方法

    class Test(object):
        def __init__(self,name,age):
            self.__name = name
            self.__age = age
    
        @classmethod
        def getInstance(cls):
            return cls("薇恩",16)
    

    绑定到类的方法,必须在定义函数的上方使用 @classmethod 装饰器,同时,第一个参数必须是 cls,这个 cls 代表这个类的类型,如上面例子中的Test,所以在类方法内部可以用 cls(参数) 创建对象,也可以用它调用属于类的属性、其他可使用的方法。对外,类方法可以通过类调用,也可以通过实例对象来调用类方法。

    静态方法

    class Test(object):
        def __init__(self,name,age):
            self.__name = name
            self.__age = age
    
        @staticmethod
        def static_func():
            print("正在调用静态方法")
    

    静态方法不像前两个有特定参数,比较自由,即使你强行把第一个参数名写出selfcls,也不会有相应的作用。静态方法可以通过类或者实例对象调用。

    property方法

    内置函数property()是用来使类的属性能像Java Bean那样操作的函数,它的函数定义为:

    property(fget=None, fset=None, fdel=None, doc=None)
    
    • fget:    function to be used for getting an attribute value
    • fset:    function to be used for setting an attribute value
    • fdel:    function to be used for deleting an attribute
    • doc:    docstring
    class C(object):
        def getx(self): return self._x
        def setx(self, value): self._x = value + 1
        def delx(self): del self._x
        x = property(getx, setx, delx)
    
    c = C()
    c.x = 5
    print(c.x)  # 6
    del c.x
    # print(c.x) AttributeError: 'C' object has no attribute '_x'
    

    这样,既能使对象能简单调用属性,也能保证数据的封装,可是,这么写太麻烦,普遍用的形式是使用装饰器来实现Bean:

    class Hero(object):
        def __init__(self,name):
            self.__name = name
    
        @property
        def name(self):
            print("getter方法...")
            return self.__name
    
        @name.setter
        def name(self,value):
            print("setter方法...")
            self.__name = value
    
        @name.deleter
        def name(self):
            print("deleter方法...")
            del self.__name
    
    p = Hero("蝙蝠侠")
    p.name                      # getter方法...
    p.name = "游城十代"          # setter方法...
    del p.name                  # deleter方法...
    

    一般@property只用于私有属性“公有化”,并且getter方法和deleter方法只能有self参数。

    类的特殊成员

    __doc__

    表示类的描述信息

    class Foo(object):
        '''Foo的描述'''
    
    print(Foo.__doc__)					# Foo的描述
    

    __module____class__

    • __module__表示当前操作的对象在那个模块
    • __class__表示当前操作的对象的类是什么,也就是谁创建了这个类,metaclass还是type
    class Foo(object):
        pass
    
    print(Foo.__module__)					# __main__
    print(Foo.__class__)					# <class 'type'>
    

    __all__

    __all__是一个字符串list,用来定义模块中对于from XXX import *时要对外导出的符号,即要暴露的借口,但它只对import *起作用,对from XXX import XXX不起作用

    __all__ = ['MNIST', 'FashionMNIST', 'CIFAR10', 'CIFAR100',
               'ImageRecordDataset', 'ImageFolderDataset']
    

    __init__

    构造方法,创建对象时自动执行的初始化函数

    class Foo(object):
        def __init__(self):
            print("init方法...")
    
    f = Foo()  # init方法...
    

    __del__

    • 析构方法,当对象在内存中被释放时,自动触发执行
    • 此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
    class Foo:
        def__del__(self):
            pass
    

    __call__

    • 对象后面加括号,触发执行
    • 构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
    class Demo(object):
        def __init__(self):
            print("执行init...")
    
        def __call__(self, *args, **kwargs):
            print("执行call...")
    
    d = Demo()  # 执行init...
    d()  # 执行call...
    

    __dict__

    • 返回类或对象中的所有成员
    • 普通字段属于对象,静态字段和方法属于类
    class Province(object):
        country = 'China'
    
        def __init__(self, name, count):
            self.name = name
            self.count = count
    
        def func(self, *args, **kwargs):
            print('func')
    
    print(Province.__dict__)					# 返回类的所有成员
    '''
    {'__module__': '__main__', 'country': 'China', 
    '__init__': <function Province.__init__ at 0x0000022A0C5278C8>, 
    'func': <function Province.func at 0x0000022A0C527950>, 
    '__dict__': <attribute '__dict__' of 'Province' objects>, 
    '__weakref__': <attribute '__weakref__' of 'Province' objects>,
     '__doc__': None}
    '''
    
    print(Province("卡兹克",1).__dict__)		# 返回对象的成员
    '''
    {'name': '卡兹克', 'count': 1}
    '''
    

    __str____repr__

    • 都是更改类的显示方式
    • __str__是给用户看到的字符串,__repr__是给开发者看的,比如debug时,变量列表显示的是__repr__函数返回的内容
    • 一般情况下只用写一个__str__,然后__repr__ = __str__
    class Text(object):
        def __init__(self,text):
            self.__text = text
    
        def __str__(self):
            return self.__text
        __repr__ = __str__  # 一般都这么写偷懒
    
    t = Text("无极剑圣")
    print(t)  # 无极剑圣
    

    __getitem____setitem____delitem__

    • 用于索引操作,如字典。以上分别表示获取、设置、删除数据
    • __getitem__也可以传入切片
    class Subject(object):
        def __getitem__(self, item):  # 根据item返回一个值
            print("调用getitem...")
    
        def __setitem__(self, key, value):  # 设置key对应的值为value
            print("调用setitem...")
    
        def __delitem__(self, key):	  # 删除这组键值对
            print("调用delitem...")
    
    m = Subject()
    n = m['math']  # 调用getitem...
    m['math'] = 100  # 调用setitem...
    del m['math']  # 调用delitem...
    

    __iter____next__

    用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了__iter____next__

    class Fib(object):
        def __init__(self):
            self.a, self.b = 0, 1  # 初始化两个计数器a,b
    
        def __iter__(self):
            return self  # 实例本身就是迭代对象,故返回自己
    
        def next(self):
            self.a, self.b = self.b, self.a + self.b  # 计算下一个值
            if self.a > 100000:  # 退出循环的条件
                raise StopIteration();
            return self.a  # 返回下一个值
    

    __slots__

    一般情况下,可以任意地给对象添加属性,这会造成很大的漏洞,影响程序安全,为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性

    class Student(object):
        __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
    
    s = Student()
    s.name = 'Michael'
    s.age = 25
    s.score = 99  # AttributeError: 'Student' object has no attribute 'score'
    

    显然,这时候不能添加限制以外的属性,但要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类的属性不做限制,除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身__slots__加上父类__slots__

    其他

    例如各种重载运算符的函数,需要用到Google即可

    创建对象

    传统创建类

    class Demo(object):
        def __init__(self,name):
            self.__name = name
    
    d = Demo("乐芙兰")
    

    d是通过Demo类实例化的对象,其实不仅d是一个对象,Demo本身也是一个对象,因为在Python中,一切皆对象,d是通过执行Demo类的构造方法创建,那么Demo也应该是通过某个类的构造方法创建。

    print(type(d))  # <class '__main__.Demo'>    表示d由Demo类创建
    print(type(Demo))  # <class 'type'>          表示Demo由type类创建
    

    所以,d对象是Demo类的一个实例,Demo类对象是 type 类的一个实例,即:Demo类对象是通过type类的构造方法创建。

    type创建类

    • 语法:type('类名',父类的元组,成员字典)
    def init(self,name):
        self.__name = name
    
    def show(self):
        print(self.__name)
    
    Demo = type('Demo',(object,),{'__init__':init,'output':show,'a':3})
    d = Demo("诡术妖姬")
    print(d)  # <__main__.Demo object at 0x0000017F3C0F32E8>
    d.output()  # 诡术妖姬
    
    • 一般用类名同名的变量来接受创建的类
    • 父类只有object时,注意元组单元素时的逗号,成员字典中,成员名是字符串,对应的值可以是方法地址,可以是属性值
    • initshow这些方法可以在前面加 @classmethod 或者 @staticmethod 等,来定义类函数和静态函数

    __new__方法

    __new__方法是类自带的一个方法,可以重写,__new__方法在实例化的时候也会执行,并且先于__init__方法之前执行,简单理解,创建对象和初始化对象。

    class Foo(object):
        def __init__(self, name):
            self.name = name
            print("Foo __init__")
    
        def __new__(cls, *args, **kwargs):
            print("Foo __new__", cls, *args, **kwargs)
            return object.__new__(cls)
    
    f = Foo("凯特琳")
    """
    Foo __new__ <class '__main__.Foo'> 凯特琳
    Foo __init__
    """
    

    重写__new__方法

    • 重写时,必须要调用父类的_new__方法,不然会覆盖父类的__new__方法,实例创建不了
    • __new__()方法创建出该类的实例,然后返回该实例给__init__()方法调用。__init__()方法的self就是__init__()方法创建返回的
    • 依照Python官方文档的说法,__init__()方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径,还有就是实现自定义的metaclas

    继承不可变对象的例子

    假如我们需要一个永远都是正数的整数类型,通过继承int

    class PositiveInteger(int):
    
        def __new__(cls, value):
            return super(PositiveInteger, cls).__new__(cls, abs(value))
    
    
    i = PositiveInteger(-3)
    
    print(i)  # 3
    

    这时候可能有的人会通过__init__方法来使-3变为3,但是,因为继承于int,是不可变的,__new__创建完时,值已经确定了,再通过__init__修改是不行的。

    __new__方法与__init__方法的关系

    class A(object):
        def __init__(self):
            print(self)
            print("这是__init__()方法...")
    
        def __new__(cls):
            print(id(cls))
            print("这是__new__()方法...")
            ret = super().__new__(cls)
            print(ret)
            return ret
    
    
    print(id(A))
    a = A()
    '''
    输出:
    	2151279654824
    	2151279654824
    	这是__new__()方法...
    	<__main__.A object at 0x000001F4E4406FD0>
    	<__main__.A object at 0x000001F4E4406FD0>
    	这是__init__()方法...
    '''
    

    单例模式

    class Number(object):
        __instance = None
    
        def __new__(cls, val):
            if cls.__instance is None:
                cls.__instance = super().__new__(cls)
                cls.__instance.__value = val
                return cls.__instance
            else:
                return cls.__instance
    
        def __str__(self):
            return str(self.__value)
    
    
    m = Number(-3)
    n = Number(-6)
    
    print(m)  # -3
    print(n)  # -3
    
    print(id(m))  # 2199891453768
    print(id(n))  # 2199891453768
    

    metaclass

    • metaclass,直译为元类,简单的解释就是,当我们定义了类以后,就可以根据这个类创建出实例,所以,先定义类,然后创建实例。但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以,先定义metaclass,然后创建类,连接起来就是:先定义metaclass,就可以创建类,最后创建实例。所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。

    • metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,你不会碰到需要使用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)
    
    class MyList(list,metaclass=ListMetaclass):
        pass
    

    当我们写下metaclass = ListMetaclass语句时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.__new__()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义

    测试一下MyList是否可以调用add()方法:

    l = MyList()
    l.add(1)
    print(l)  # [1]
    

    元类中,最好只定义__new__()方法,因为其他类如果使用了“metaclass = XXXMetaclass”,只会调用这个metaclass__new__()方法来创建类,也就是避免了直接创建类,在创建类之前还封装了一组操作,但是,metaclas中其他的方法、属性等,是不会让使用metaclass创建的类继承的,所以一般在元类中,只写__new__()方法。metaclass可以隐式继承到子类

  • 相关阅读:
    【Hadoop环境搭建】Centos6.8搭建hadoop伪分布模式
    【Java安装】Centos6.8 安装Java1.6
    【SVN】win7 搭建SVN服务器
    【KVM】Ubuntu14.04 安装KVM
    【VNC】Ubuntu14.04LTS下安装VNC View
    【虚拟化】支持IDE/SATA/SCSI
    【KVM安装】在Centos6.8中安装KVM
    【脚本】新增未扩展磁盘容量
    【SVN】自动定时更新
    【GIS】地球经纬度和米换算(转)
  • 原文地址:https://www.cnblogs.com/ice-coder/p/12619214.html
Copyright © 2011-2022 走看看