美文网首页那年夏天
Python3 面向对象

Python3 面向对象

作者: 坚持到底v2 | 来源:发表于2019-01-11 10:39 被阅读0次

    1. 简单示例

    class Student(object):
        """
        一个简单的类实例
        """
    
        def __init__(self, name, score):
            super(Student,self)
            self.name = name
            self.score = score
    
        def print_score(self):
            print('%s: %s' % (self.name, self.score))
    
    
    # 实例化类
    x2 = Student('Bart Simpson', 59)
    x2.print_score()
    #  输出 Bart Simpson: 59
    
    

    2. 函数

    • 类的实例方法与普通的函数有一个特别的区别--它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self , 表示对象自身

    • 类还有静态方法和类方法,分别使用 @staticmethod 和 @classmethod 修饰,

      • 对于静态方法,参数没有要求,
      • 对于类方法,第一个参数一般使用 cls 表示是类本身

    2.1 例子:

    class MyClass:
       @classmethod
       def f1(cls):
         return str(cls.y)
    
       def f2(self):
         return str(self.y)
    
    
    x = MyClass()
    MyClass.y=1
    x.y=12
    # 调用 类方法f1 输出 1
    print(MyClass.f1()=='1')
    print(x.f1()==MyClass.f1())
    
    # 调用实例方法f2 输出 12
    print(x.f2()=='12')
    
    # 类直接调用 实例方法会报异常
    try:
         MyClass.f2()
    except:
         print("MyClass 调用 实例方法f2 报错")
    
    

    3. 私有属性和方法

    使用 __ 开头的属性和方法 不能被子类继承

    其实是因为Python解释器对外把 __name 变量改成了 _ClassName__name ,
    但是强烈建议你不要基于这个机制去访问私有属性,因为不同版本的Python解释器可能会把 __name 改成不同的变量名。
    总的来说就是,Python本身没有任何机制阻止你干坏事,一切全靠自觉.
    另外不要直接设置类似于 __name 这样的属性(因为类可以随意扩展属性), 因为前面说了 Python解释器 已经把 __name 改成了 _ClassName__name , 你这样做相当于是扩展了一个新的 __name 属性而已, 并不是修改的私有属性 __name(也就是_ClassName__name)

    3.1 专有方法:

    • __init__
    • __del__ : 析构函数
    • __repr__ : 打印,转换
    • __setitem__ : 按照索引赋值
    • __getitem__ : 按照索引获取值
    • __len__ : 获得长度
    • __cmp__ : 比较运算
    • __call__ : 函数调用
    • __add__ : 加运算
    • __sub__ : 减运算
    • __mul__ : 乘运算
    • __div__ : 除运算
    • __mod__ : 求余运算
    • __pow__ : 乘方

    4. 继承

    class DerivedClassName(BaseClassName1,BaseClassName2):
        pass
    
    

    如果基类中有相同的方法名,而在子类使用时未指定,python从左至右搜索
    即方法在子类中未找到时,按从左到右的顺序查找基类中是否包含方法

    5. 探索对象 dir()

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

    print(dir('ABC'))
    # 输出 ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
    
    print(len('ABC'))  # 等价于 'ABC'.__len__()
    
    

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

    hasattr(obj, 'x') # 有属性'x'吗?
    # True
    
    setattr(obj, 'y', 19) # 设置一个属性'y'
    
    getattr(obj, 'y') # 获取属性'y'
    # 19
    
    getattr(obj, 'z', 404) # 获取属性'z',如果不存在,返回默认值404 , 而不是抛出异常
    # 404
    

    当探索一个类实例的某个对象的属性时, 如果该对象没有此属性, 则会使用其所属类的类属性, 并可能应该会按照这个逻辑一直递归到顶层.

    class Student(object):
        # name 属性是属于Student类共有的
        name="Student"
    
    s = Student()
    
    print(s.name) # 此时 s对象没有 name 属性, 所以使用 Student 类的属性
    
    s.name='myName' # 设置后 s对象由了 name 属性
    
    del s.name # 删除 s对象的name属性
    
    

    6. 使用 __slots__

    正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。

    class Student(object):
        pass
    
    def set_age(self, age): # 定义一个函数作为实例方法
        self.age = age
    
    s = Student()
    
    from types import MethodType
    s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
    
    s.set_age(25) # 调用实例方法
    print(s.age) # 测试结果
    
    s2 = Student()
    Student.set_age = set_age # 可以给class绑定方法 后,所有该类的对象都可以使用set_age
    s2.set_age(25) # 调用实例方法
    print(s2.age) # 测试结果
    
    

    但是,如果我们想要限制实例的属性怎么办?
    比如,只允许对Student实例添加name和age属性。

    为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的 __slots__ 变量,来限制该class实例能添加的属性:

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

    使用 __slots__ 要注意, __slots__ 定义的属性仅对当前类实例起作用,对继承的子类是不起作用的, 这也是显而易见的,
    除非在子类中也定义 __slots__ ,这样,子类实例允许定义的属性就是自身的 __slots__ 加上父类的 __slots__

    7 使用@property

    class Student(object):
    
        _score=None
    
        @property
        def score(self):
            return self._score
    
        @score.setter
        def score(self, value):
            # some code
            self._score = value
    
    s=Student()
    s.score=100
    print(s.score)
    
    

    8. 使用枚举类

    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)
    
    # 输出
    """
    Jan => Month.Jan , 1
    Feb => Month.Feb , 2
    Mar => Month.Mar , 3
    Apr => Month.Apr , 4
    May => Month.May , 5
    Jun => Month.Jun , 6
    Jul => Month.Jul , 7
    Aug => Month.Aug , 8
    Sep => Month.Sep , 9
    Oct => Month.Oct , 10
    Nov => Month.Nov , 11
    Dec => Month.Dec , 12
    """
    
    

    value属性则是自动赋给成员的int常量,默认从1开始计数。

    如果需要更精确地控制枚举类型,可以从Enum派生出自定义类:

    from enum import Enum, unique
    
    # @unique装饰器可以帮助我们检查保证没有重复值。
    @unique
    class Weekday(Enum):
        Sun = 0 # Sun的value被设定为0
        Mon = 1
        Tue = 2
        Wed = 3
        Thu = 4
        Fri = 5
        Sat = 6
    
    

    9. 使用元类

    9.1 type

    type() 不仅可以返回一个对象的类型,还可以创建出新的类型.

    正常情况下,我们都用 class Xxx... 来定义类,但是,type()函数 也允许我们动态创建出类来,
    也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同,
    要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,会非常复杂。

    示例

    def fn(self, name='world'): # 先定义函数
        print('Hello, %s.' % name)
    
    # 使用 type 函数创建 Hello class
    # 第一个参数是 类名称
    # 第二个参数是继承的父类集合
    # 第三个参数是class的方法名称与函数绑定,这里我们把 函数fn 绑定到方法名 hello上
    Hello = type('Hello', (object,), dict(hello=fn)) 
    
    h = Hello()
    
    h.hello()
    # 输出 Hello, world.
    
    

    9.2 metaclass

    metaclass 允许你创建类或者修改类。
    换句话说,你可以把类看成是 metaclass 创建出来的“实例”。

    待续

    相关文章

      网友评论

        本文标题:Python3 面向对象

        本文链接:https://www.haomeiwen.com/subject/ehwtdqtx.html