zoukankan      html  css  js  c++  java
  • python面向对象笔记

    一、封装(属性/私有方法/公有方法/静态方法/构造函数...)

    # 定义一个类
    class Animal:
        # 私有成员(用_开头的约定为私有成员 - 注:仅仅是君子协定)
        _age = 0
    
        # 构造函数
        def __init__(self, name):
            # 建议所有私有成员在这里初始化(不管是已定义的,还是"动态"添加的)
            self.name = name  # 动态添加的私有成员
            self._age = 1  # 已经定义好的私有成员
    
        # 静态方法
        @staticmethod
        def eat():
            print("Animal can eat food")
    
        # 公有方法
        def to_string(self):
            return "name:" + self.name
    
        # 私有方法(用_开头的约定为私有方法 - 注:仅仅是君子协定)
        def _some_method(self):
            return "this is a private method of :" + self.name
    
        # setter示例
        def set_age(self, age):
            if age < 0 or age > 100:
                print("age is invalid!")
            self._age = age
    
        # getter示例
        def get_age(self):
            return self._age
    
        def run(self):
            print(self._some_method())
            print("I am running")
    
        def test(self):
            print("test1")
    
        # 注:这样会覆盖上面的test(self版本)
        def test(self, hello):
            print("test2:" + hello)
    
    
    animal = Animal("A new animal")
    print(animal.to_string())  # 方法调用
    animal.eat()  # 静态方法调用1(不推荐)
    Animal.eat()  # 静态方法调用2
    print("animal._age=" + str(animal.get_age()))
    animal.set_age(10)  # 调用setter
    print(animal.get_age())  # 调用getter,输出10
    animal.run()  # 公有方法里调用私有方法
    animal._age = 30  # 直接修改私有成员(不推荐)
    animal._age2 = 40  # 注意:这里实际上给实例animal"动态"添加了一个_age2属性
    print(animal.get_age())  # 这里输出的仍然是10
    print("_age2:" + str(animal._age2))  # 这里输出的动态添加的_age2属性值
    print(animal.test("hi"))
    #print(animal.test())  # 这里会报错,因为test(self,hello)这个版本要求hello参数有值
    print(animal._some_method())  # 直接调用私有方法(不推荐)
    

    输出:

    name:A new animal
    Animal can eat food
    Animal can eat food
    animal._age=1
    10
    this is a private method of :A new animal
    I am running
    30
    _age2:40
    test2:hi
    None
    this is a private method of :A new animal
    

    几个要点:

    1、约定大于配置:比如构造函数约定为__init__;私有方法/成员约定为"_"开头(注:只是君子协定,硬要调用的话,外部是可以直接调用的);实例方法约定第1个参数为self  

    2、重载的处理,不象java里要定义多个方法签名,python里就一个版本,但是通过可变参数来实现(后面还要细讲)

    3、动态语言,实例在运行过程中,可随时动态添加属性

    另外注意:不要轻易使用__(二个连续的下划线)做为方法或变量的前缀或后缀,"__XXX__"在python里通常有特定含义,如果使用"__"开头的变量或写法,可能会遇到各种奇葩问题。

    二、重载

    传统的OOP语言,比如java,重载只能是定义多个不同方法签名的method,来实现重载,但是python可以做得更简单,用参数默认值就可以变相实现。

    # 定义一个类
    class Calculator:
        # 加法
        def add(self, a=0, b=0, c=0, d=0):
            print("args:", a, b, c, d)
            return int(a) + int(b) + int(c) + int(d)
    
    
    cal = Calculator()
    print(cal.add(1, 2))
    print(cal.add(1, 2, 3))
    print(cal.add(1, "2", 3, "4"))
    
    print("
    ")
    
    cal = Calculator #注意这里,如果漏了(),结果会大不同
    print(cal.add(1, 2))
    

     输出:

    args: 1 2 0 0
    3
    args: 1 2 3 0
    6
    args: 1 2 3 4
    10
    
    
    args: 2 0 0 0
    2  

    注意:16行,如果漏了(),cal = Calculator,实际上只是cal只是相当于类Calculator的别名,这样调用add(1,2)时,类似于静态方法调用,1会认为是第一个参数self,所以输出就成了2.

    如果不确定参数个数,还可以这么做:

    # 定义一个类
    class Calculator:
        # 约定:*参数,表示参数个数不限定
        def add(self, *args):
            result = 0
            for arg in args:
                result += int(arg)
            return result
    
    
    cal = Calculator()
    print(cal.add(1, 2, 3))
    print(cal.add("1", 2, "3"))
    

    输出:

    6
    6
    

      

    三、继承

    3.1 基本示例

    class Fruit:
        def __init__(self, name):
            print("Fruit constructor...")
            self.name = name
    
        def to_string(self):
            print("Fruit to_string...")
            return "name:" + self.name
    
        # 抽象方法
        def get_color(self):
            print("Fruit get_color...")
            raise NotImplementedError
    
    
    class RedApple(Fruit):
        def __init__(self, name):
            print("Apple constructor...")
            # 调用父类的构造函数
            Fruit.__init__(self, name)
    
        def get_color(self):
            return self.name + " is red"
    
    
    fruit = Fruit("unknown")
    print(fruit.to_string())
    # print(fruit.get_color())  # 报错,因为没实现
    print("
    ")
    
    redApple = RedApple("red apple")
    print(redApple.get_color())
    print(redApple.to_string())
    
    print("
    ")
    print("1、redApple is instance of RedApple ? ", isinstance(redApple, RedApple))
    print("2、redApple is instance of Fruit ? ", isinstance(redApple, Fruit))
    print("3、fruit is instance of Fruit ? ", isinstance(fruit, Fruit))
    print("4、fruit is instance of RedApple ? ", isinstance(fruit, RedApple))
    print("5、RedApple is subclass of Fruit ? ", issubclass(RedApple, Fruit))
    print("6、Fruit is subclass of Fruit ? ", issubclass(Fruit, Fruit))
    print("7、Fruit is subclass of RedApple ? ", issubclass(Fruit, RedApple))
    

     输出:

    Fruit constructor...
    Fruit to_string...
    name:unknown
    
    
    Apple constructor...
    Fruit constructor...
    red apple is red
    Fruit to_string...
    name:red apple
    
    
    1、redApple is instance of RedApple ?  True
    2、redApple is instance of Fruit ?  True
    3、fruit is instance of Fruit ?  True
    4、fruit is instance of RedApple ?  False
    5、RedApple is subclass of Fruit ?  True
    6、Fruit is subclass of Fruit ?  True
    7、Fruit is subclass of RedApple ?  False
    

    注:抽象方法是通过抛出未实现的异常来实现的。如果想类似java定义抽象类,把__init__方法抛出未实现异常就行。

    3.2 多继承

    python支持多继承,这点与java有很大区别

    class P1:
        def a(self):
            print("P1-a")
    
        def b(self):
            print("P1-b")
    
        def x(self):
            print("P1-x")
    
    
    class P2:
        def a(self):
            print("P2-a")
    
        def b(self):
            print("P2-b")
    
        def y(self):
            print("P2-y")
    
    
    # 多继承示例
    class S1(P1, P2):
        def a(self):
            print("S1-a")
    
    
    class S2(P2, P1):
        def a(self):
            print("S2-a")
    
    
    s1 = S1()
    s1.a()
    s1.b()  # P1-b
    s1.x()
    s1.y()
    
    print("
    ")
    
    s2 = S2()
    s2.a()
    s2.b()  # P2-b
    s2.x()
    s2.y()
    
    print("
    ")
    
    print("s1 isinstance of P1:", isinstance(s1, P1))
    print("s1 isinstance of P2:", isinstance(s1, P2))
    print("s1 isinstance of S1:", isinstance(s1, S1))
    print("s1 isinstance of S2:", isinstance(s1, S2))
    

    输出:

    S1-a
    P1-b
    P1-x
    P2-y
    
    
    S2-a
    P2-b
    P1-x
    P2-y
    
    
    s1 isinstance of P1: True
    s1 isinstance of P2: True
    s1 isinstance of S1: True
    s1 isinstance of S2: False
    

    注意多承继的顺序,如果“爸爸们”之间有重名方法,将会按照继承顺序,谁在前面,就调用谁的。eg:def S(A,B) 子类S继承自A,B,如果A,B中都有方法x,调用S.x时,因为A排在B的前面,所以调用到的就是A.x方法。从上面的输出就可以得到印证。

    3.3 接口、abc模块、属性

    3.1中抽象类/方法是通过抛出异常来实现的,有点粗暴,下面介绍一种更优雅的方法,python内置的abc模块

    from abc import *
    
    
    # 接口示例
    class IRun(ABC):
        # 抽象方法
        @abstractmethod
        def run(self):
            pass
    
    
    class Animal(ABC):
        # 抽象属性
        @property
        @abstractmethod
        def name(self):
            pass
    
    
    class Dog(Animal, IRun):
        def __init__(self, name):
            self._name = name;
    
        # 属性的getter
        @property
        def name(self):
            return self._name
    
        # 属性的setter (注:每个property都会生成一个对应的@xxx.setter)
        @name.setter
        def name(self, value):
            self._name = value
    
        # 接口的方法实现
        def run(self):
            print(self.name + " is running")
    
    
    dog1 = Dog("a")
    dog1.run()
    dog2 = Dog("b")
    dog2.run()
    print(isinstance(dog1, IRun))
    print(id(dog1), id(dog2), id(dog1) == id(dog2))  # 判断2个实例是否相等
    

      输出:

    a is running
    b is running
    True
    4314753736 4314753792 False
    

    最后送一波福利: https://github.com/faif/python-patterns  这是python实现的所有设计模式,对oop感兴趣的推荐研究。

  • 相关阅读:
    PHP 指定的 CGI 应用程序由于未返回完整的一组 HTTP 头而产生错误行为。
    BPM触发事件
    封装继承多态到底讲的是什么
    .Net 为什么叫.Net 转载自 jerrylsxu 的博客
    C# 琐碎记忆 Message
    SQL Case
    C# 命名规范(部分)
    C# 反射
    C# 日志 log 配置文件
    C# 琐碎记忆 三元表达式
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/python-object-oriented-programming.html
Copyright © 2011-2022 走看看