面向对象特性与类的重要属性

 

1、面向对象的特性

1. 回顾封装

Python不依赖语言的特性去封装数据,而是经过遵循必定的数据属性或函数属性的命名来达到封装的效果。任何以单下划线开头的名字都应该是内部的、私有的。封装的意义在内部业务逻辑的数据隐藏。Python真正意义上的封装是用类的内外访问来区分的。而且它与Java的private属性有很大的区别,Python并无强制性的拒绝外部类对私有属性的访问。换句话说,以单下划线或双下划线开头的私有属性仅仅只是一种约定。java

(1)第一层封装: 使用以单下划线开头的私有属性。对象在调用时可直接使用“对象名 . 私有属性名”;python

(2)第二层封装:使用以双下划线开头的私有属性。对象在调用时不能直接使用“对象名 . 私有属性名”。须要在双下划线开头的私有属性前加上“_类名”,如:“对象名 .  _类名 . 私有属性名”。算法

(3)第三层封装:使用自定义的函数来提供接口。明确区份内外,实现内部业务逻辑的封装,并像java的setter和getter方法同样给出外部程序的使用自定义的接口函数,让该接口函数返回一个值私有的性。数据库

2. Python的继承

Python的继承与C++的多继承很是类似,它的继承关系会被解释器解释为一个MRO列表,该MRO列表就是一个简单的全部基类的线性顺序列表。MRO列表的构造是经过一个C3线性化算法来实现的。Python和java同样,全部类的最终基类是Object。继承从某种角度上来说是有害的,继承将类与类之间耦合到一块了。这大大的破坏了系统的封闭原则。继承真正有意义的是接口式的继承。Python提供的接口式的继承。Python 的继承顺序可由类使用__mro__查看。解释器查看顺序遵循如下三条规则:数组

(1)子类会先于父类被检查
(2)多个父类会根据他们在列表中的顺序进行检查
(3)若是对下一个类存在两个合法的选择,选择第一个父类ide

class Person:
        """
        描述人的类
        """

        def __init__(self, name, gender, age):
            self.name = name
            self.gender = gender
            self.age = age

        def printinfo(self):
            print("人类")


    class Student(Person):
        """
        学生的类
        """
        def __init__(self, name, gender, age, school):
            """
            使用super调用父类的构造函数时,参数中不能出现self,由于
            super已经默认给出了self,若是此时加上self会报错。而报错
            的缘由是多了一个参数。使用super()调用父类的函数使程序更
            加灵活。固然也可使用如下耦合的调用模式。
            Person.__init__(self, name, gender, age)

            """
            super().__init__(name, gender, age)
            self.school = school

        def ptintinfo(self):
            print(self.name, self.gender, self.age, self.school)


    s = Student("Macky", "", 18, "麻省理工")
    s.ptintinfo()
View Code

3. 多态性

 Python的多态和java的多态同样。多态的概念指出了对象如何经过他们共有的属性及函数来访问,不须要考虑他们之间的具体的类。函数

class Person:

        def __init__(self, name, age):
            self.name = name
            self.age = age

        def behavior(self):
            if self.age >= 0 and self.age <= 18:
                print("%s是未成年人"%self.name)
            else:
                print("%s是成年人"%self.name)

    class Adult(Person):
        pass

    class Pupil(Person):
       pass

    #实现多态的简单例子
    def func(obj):
        obj.behavior()

    #初始化Adult和Pupil
    adult = Adult("小明",3)
    pupil = Pupil("小华",20)

    #调用func函数
    func(adult)
    func(pupil)
View Code

 

2、duck类型

python的duck类型,不少人把它称为反射。反射的概念是由Smith在1982年首次提出的,主要是指程序能够访问、检测和修改它自己状态或行为的一种能力(自省)。这一律念的提出很快引起了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。反射展现了某对象在运行期间是如何取得本身的状态的。若是传一个对象给你,你能够查出他的全部能力,那是极好的。若是Python不支持某种形式的反射功能,dir和type内置函数,将很难工做。还有那些特殊的属性,像__dict__、__name__及__doc__。反射的好处在于它能够事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这实际上是一种“后期绑定”,你能够事先把主要的逻辑写好(只定义接口),而后后期再去实现接口的功能。python的反射比起java的反射更加灵活,用着用着就能体会到。测试

duck提供了四个很是重要的函数,这四个函数贼鸡儿六。spa

hasattr

 判断指定对象中有没有一个name字符串对应的方法或属性,若是有能够继续使用getatt操作系统

 

getattr

 获取对象的属性值或这函数的地址。当获取对象的属性值不存在时返回None,而函数不存在时报错。若是要想函数不存在时不报错能而且能通知咱们函数不存在,恶可使用default参数。该函数的功能与对象调用属性相同。如:getattr(x, 'y')   <==>   x.y

 

setattr

 为对象x的属性y设置或修改一个值,当属性不存在时,会将setattr设置的不存在的属性向对象的字典中追加。如:setattr(x, y, v)   <==>   x.y =v

 

delattr

 删除对象的属性。如:delattr(x, y)   <==>   del x.y

class Person:

        def __init__(self, name, gender, age):
            self.name = name
            self.gender = gender
            self.age = age

        def eating(self):
            print("在吃饭")

        def sleep(self):
            print("在睡觉")


    person = Person("Handy","",18)

    #使用hasattr判断对象中是否存在属性name
    print(hasattr(person, "name"))  #输出:True

    #使用getattr获取对象的属性值
    print(getattr(person, "name"))  #输出:Handy

    #使用getattr获取对象的函数地址
    print(getattr(person, "eating"))#输出:eatting()的地址

    #运行函数
    getattr(person, "eating")()     #输出:在吃饭

    #使用getattr的default参数
    print(getattr(person, "eatingfood", "函数不存在"))    #输出:函数不存在

    #使用setattr修改一个值
    setattr(person, "name", "Lily")
    print(person.__dict__)

    #向对象的字典追加一个不存在的值
    setattr(person, "ID", "001")
    print(person.__dict__)

    #增长函数1
    setattr(person, "func", lambda x : x+1)
    print(person.func)
    print(person.func(10))

    #增长函数2
    setattr(person, "func", lambda self : self.name+"是能够这样作的")
    print(person.func)
    print(person.func(person))

    #使用delattr删除对象的属性
    delattr(person, "ID")
    print(person.__dict__)
View Code

 

3、类的重要属性

 在类中包含着不少默认的内置属性,当咱们建立一个类时,若是有必要,咱们能够进行重载。Python为咱们提供了标准数据类型,以及丰富的内置函数,其实在不少场景下咱们都须要基于标准数据类型来定制咱们本身的数据类型,新增或者改写方法,这就用到了继承/派生知识。

 attr系列

_ _getattr_ _            属性不存在时自动触发

_ _setattr_ _            设置属性时自动触发

_ _delattr_ _            删除属性时自动触发

_ _getattribute_ _    程序调用时(不管属性存在与否),当它使用raise关键字抛出AttributeError异常时,由_ _getattr_ _捕捉处理

    class Test:

        def __init__(self, book_name):
            self.book_name = book_name

        #属性不存在时会自动触发
        def __getattr__(self, item):
            print("访问的属性不存在时执行的操做")

        #初始化时会执行
        def __setattr__(self, key, value):
            if type(value) is str:
                #value先转换一下在设置,这样比较好玩
                self.__dict__[key] = value.upper() 
                print("已经设置")
            else:
                print("参数必须是str")

        #删除属性时会自动触发
        def __delattr__(self, item):
            print("执行删除属性【%s】的操做"%item)

    #测试
    test = Test("从你的世界路过")   #输出:已经设置
    print(test.__dict__)            #输出:{'book_name': '从你的世界路过'}
    del test.book_name              #输出:执行删除属性【book_name】的操做
    print(test.__dict__)            #输出:{'book_name': '从你的世界路过'}
            
View Code

 __del__

 当对象在内存中被释放时,自动触发执行。若是产生的对象仅仅只是python程序级的(用户级),那么无需定义_ _del_ _,若是产生的对象的同时还会向操做系统发起系统调用,即一个对象有用户级与内核级两种资源,好比(打开一个文件,建立一个数据库连接),则必须在清除对象的同时回收系统资源,这就用到了_ _del_ _。须要注意的是,咱们删除实例的属性时是不会的执行_ _del_ _的,只有在删除实例时才会触发_ _del_ _。系统在回收内存时也会触发它。

 好比当咱们建立数据库类时,用该类实例化出数据库连接对象,对象自己是存放于用户空间内存中,而连接则是由操做系统管理的,存放于内核空间内存中。当程序结束时,python只会回收本身的内存空间,即用户态内存,而操做系统的资源则没有被回收,这就须要咱们重载del,在对象被删除前向操做系统发起关闭数据库连接的系统调用,回收资源。

    class Person:

        def __init__(self, name):
            self.name = name

        def __del__(self):
            print("执行删除实例的操做")

    #测试
    person = Person("SB")
    del person.name #不执行
    del person      #执行删除实例的操做
View Code

 __slots__

Python为每个实例提供了一个独自的字典。咱们经常使用的“.”运算符在底层其实操做的是字典。__slots__对全部的实例对象取消了字典,实例使用一种更加紧凑的内部表示,经过一个很小的固定大小的数组来构建,而不是为每一个实例定义一个字典,这跟元组或列表很相似。类变量列出的属性名在内部被映射到这个数组的小标上。它限制了实例对象的访问操做,当访问的属性不存在时,会直接报错。

当咱们一个类有不少的实例时,每个实例都会有一个单独的内存空间,若是使用__slots__确实是可以减小资源空间的占用,可是python不少特性都依赖于普通的基于字典的实现。而使用类变量的弊端是再也不支持基于字典的特性,好比多继承等等。

class Person:

    __slots__ = ["name","age"]


#测试
person = Person()
print(Person.__slots__)  #输出:['name', 'age']
person.name = "小明"
person.age = 18
print(person.name)      #输出:小明
print(person.age)       #输出:18
#输出:AttributeError: 'Person' object has no attribute'__dict__'
print(person.__dict__)  
View Code

 __format__

__format__内置属性是一种自定义的格式化。一般 Python自带的format格式化一般没法知足咱们的需求,所以咱们须要对其进行修改。

    #定义格式字典
    format_dic={
        'ymd':'{0.year}{0.mon}{0.day}',
      'm-d-y':'{0.mon}-{0.day}-{0.year}',
      'y:m:d':'{0.year}:{0.mon}:{0.day}'
    }
    class Date_format:

        def __init__(self,year,mon,day):
            self.year=year
            self.mon=mon
            self.day=day

        def __format__(self, format_spec):
            if not format_spec or format_spec not in format_dic:
                format_spec='ymd'
            fm=format_dic[format_spec]
            return fm.format(self)

    #测试
    date = Date_format(2016,12,26)
    print(format(date,'ymd'))      #输出:20161226
    print(format(date,'y:m:d'))   #输出:2016:12:26
    print(format(date,'m-d-y'))   #输出:12-26-2016
    print(format(date,'m-d:y'))   #输出:20161226
View Code

 

 __call__

咱们知道构造函数的执行是由建立实例对象触发的,即:对象 = 类名()。Python中一切皆对象,咱们建立的类自己也是一个对象。内置属性__call__的执行是因为对象建立后被做为函数使用而触发的。重载__call__的意义在于将对象做为函数使用,__call__不影响对象的生命周期,不影响一个对象的构造和析构。

    class Test:

        def __init__(self, name):
            self.name = name
            print("执行__init__")

        def __call__(self, *args, **kwargs):
            print("执行__call__")


    #测试
    test = Test("SB")   #输出:执行__init__
    test()              #输出:执行__call__
View Code

 

 __next__ 和 __iter__

在迭代器中咱们说实现了迭代器协议的对象称为可迭代对象。而迭代器协议强调对象必须提供一个next或_ _next_ _()方法,而且执行该方法只有两种决策,要么返回迭代中的下一项,要么者引发一个StopIteration异常,以终止迭代。其实这个协议也不过是经过内置属性实现的。咱们知道一个普通的对象是没法对它进行遍历的,可是Python提供了_ _next_ _和_ _iter_ _这两个内置属性供咱们重载对象。经过这两个内置属性的使用使得普通对象可变为可迭代对象。而迭代器的实现也基于此。

    class Test:

        def __init__(self, start, stop):
            self.start = start
            self.stop = stop

        def __iter__(self):
            return self

        def __next__(self):
            if self.start >= self.stop:
                raise StopIteration("索引越界")
            num = self.start
            self.start += 1
            return num

    #测试
    test = Test(1,10)
    for i in test: #遍历对象
        print(i, end=' ')

########################################################################################################

    #斐波那契数
    class Fib:
        def __init__(self):
            self._a=0
            self._b=1

        def __iter__(self):
            return self

        def __next__(self):
            self._a,self._b=self._b,self._a + self._b
            return self._a

    #测试
    f1=Fib()
    for i in f1:
        if i > 100:
            break
        print('%s ' %i,end='')
View Code

 

 __str__ 和 __repr__

当咱们使用str函数并以对象做为参数的时候,返回的是一个对象的地址及所属类。它的默认执行路径是:str( obj )  ==>  obj._ _str_ _()。str 与 repr 都用于控制输出,当他们在程序中共存时,repr不会执行;当程序中只有repr时,虽然它被用于解释器,可是在控制台会执行,由于str找不到,因此它是str的替代品。

    class Person:

        #系统默认输出对象的地址及所属类
        def __str__(self):
            return "修改系统默认的内置属性"


    #测试
    person = Person()
    print(person)   #输出:修改系统默认的内置属性

###########################################################################################

    class Student:

        def __init__(self, name, age):
            self.name = name
            self.age = age

        def __str__(self):
            return "名字是%s,年龄是%s"%(self.name, self.age)

        def __repr__(self):
            return "执行__repr__"

    stu = Student("小明",18)
    print(stu)     #输出:名字是小明,年龄是18
View Code
相关文章
相关标签/搜索