zoukankan      html  css  js  c++  java
  • python 面向对象编程

    所有方法的第一个参数必须是 self,self 表示类是实例本身,类似于 c++ 中的 this 指针。当然我们也可以讲 self 写成其它名字:

    1 class MyClass(object):
    2     i = 123
    3     def f(this):#self写成this也是正确的
    4         return 'hello'
    5 
    6 gel = MyClass()
    7 print(gel.i)
    8 print(gel.f())
    View Code

    构造函数

    行为基本和 c++ 中的构造函数类似,但当有多个构造函数时,只能调用最后一个构造函数,否则会报错:

     1 #!/usr/bin/python3
     2 
     3 class gel(object):
     4     def __init__(self):
     5         print("构造函数1")
     6     def __init__(self, cnt):
     7         print(cnt)
     8 
     9 # a = gel()#如果定义了多个构造函数,则只能调用最后一个,否则会报错
    10 a = gel(1)
    View Code

    注意:一个类中可以定义多个构造方法,但实例化类时只实例化最后的构造方法,即后面的构造方法会覆盖前面的构造方法,并且需要根据最后一个构造方法的形式进行实例化。因此通常一个类中只定义一个构造函数

    类的访问权限

    在 python 中,类成员变量名如果以 __ 开头,就会编程私有变或私有方法,只能在类内部访问,外部不能访问

    继承

    在继承中,基类的构造方法(__init__() 方法)不会被自动调用,需要在子类的构造方法中专门调用

    在调用基类的方法时需要加上基类的类名前缀,并带上 self 参数变量。区别于在类中调用普通函数时不需要带 self 参数

    在 python 中,首先查找对应类型的方法,如果在子类中找不到对应的方法,才到基类中逐个查找

    代码:

     1 #!/usr/bin/python3
     2 
     3 class Animal(object):
     4     def run(self):
     5         print('Animal is running...')
     6 
     7 class Dog(Animal):
     8     pass
     9 
    10 class Cat(Animal):
    11     pass
    12 
    13 dog = Dog()
    14 dog.run()
    15 
    16 cat = Cat()
    17 cat.run()
    18 
    19 # 输出:
    20 # Animal is running...
    21 # Animal is running...
    View Code

    注意:派生类中也不能调用基类的私有方法

    多态

    python 的多态不同于 c++ 中需要通过虚函数来实现。当子类和父类存在相同的方法时,运行代码时总是自动调用对应对象的版本:

     1 #!/usr/bin/python3
     2 
     3 class Animal(object):
     4     def run(self):
     5         # pass
     6         print('Animal is running...')
     7 
     8 class Dog(Animal):
     9     # pass
    10     def run(self):
    11         print('Dog is running...')
    12 
    13 class Cat(Animal):
    14     # pass
    15     def run(self):
    16         print('Cat is running...')
    17 
    18 dog = Dog()
    19 dog.run()
    20 
    21 cat = Cat()
    22 cat.run()
    23 
    24 animal = Animal()
    25 animal.run()
    26 
    27 # 输出:
    28 # Dog is running...
    29 # Cat is running...
    30 # Animal is running...
    View Code

    多重继承

    多重继承的类定义:

    class DerivedClassName(Base1, Base2, Base3):
      pass

    需要注意圆括号中父类的顺序,若父类中有相同的方法名,在子类使用时未指定,python 会从左到有搜索。

    调用对应方法时,若方法在子类中未找到,则从左到右查找父类中是否包含该方法

    代码:

     1 #!/usr/bin/python3
     2 
     3 class Animal(object):
     4     pass
     5 
     6 #大类
     7 class Mammal(Animal):
     8     pass
     9 
    10 class Bird(Animal):
    11     pass
    12 
    13 
    14 class Runnable(object):
    15     def run(self):
    16         print('running...')
    17 
    18 class Flyable(object):
    19     def fly(self):
    20         print('Flying...')
    21 
    22 class Dog(Mammal, Runnable):
    23     pass
    24 
    25 class Bat(Mammal, Flyable):
    26     pass
    View Code

    类的专有方法

    形如 __xxx__ 变量或函数名在 python 中是有特殊用途的

    除了前面的构造函数 __init__ 外还有:

    __str__ 和 __repr__

    类似于c++中重载<<运算符,将类对象按给定的格式转化成字符串

     1 class Test(object):
     2     def __init__(self, value='hello, world!'):
     3         self.data = value
     4 
     5 >>> t = Test()
     6 >>> t
     7 <__main__.Test at 0x7fa91c307190>
     8 >>> print t
     9 <__main__.Test object at 0x7fa91c307190>
    10 
    11 # 看到了么?上面打印类对象并不是很友好,显示的是对象的内存地址
    12 # 下面我们重构下该类的__repr__以及__str__,看看它们俩有啥区别
    13 
    14 # 重构__repr__
    15 class TestRepr(Test):
    16     def __repr__(self):
    17         return 'TestRepr(%s)' % self.data
    18 
    19 >>> tr = TestRepr()
    20 >>> tr
    21 TestRepr(hello, world!)
    22 >>> print tr
    23 TestRepr(hello, world!)
    24 
    25 # 重构__repr__方法后,不管直接输出对象还是通过print打印的信息都按我们__repr__方法中定义的格式进行显示了
    26 
    27 # 重构__str__
    28 calss TestStr(Test):
    29     def __str__(self):
    30         return '[Value: %s]' % self.data
    31 
    32 >>> ts = TestStr()
    33 >>> ts
    34 <__main__.TestStr at 0x7fa91c314e50>
    35 >>> print ts
    36 [Value: hello, world!]
    37 
    38 # 你会发现,直接输出对象ts时并没有按我们__str__方法中定义的格式进行输出,而用print输出的信息却改变了
    View Code

    这部分代码摘自:https://blog.csdn.net/luckytanggu/article/details/53649156

    通常__str__() 和 __repr__() 的代码是一样的,所以可以写成:

     1 #!/usr/bin/python3
     2 
     3 class Student(object):
     4     def __init__(self, name):
     5         self.name = name
     6 
     7     def __str__(self):
     8         return '学生名称:%s' % self.name
     9 
    10     __repr__ = __str__
    11 
    12 st = Student('xiao004')
    13 print(st)
    14 # 输出:
    15 # 学生名称:xiao004
    View Code

    __iter__

    如果想将一个类用于 for...int 循环,类似于 list 或 tuple 一样,就必须实现一个 __iter__() 方法。该方法返回一个迭代对象,python 的 for 循环会不断调用迭代对象的 __next__() 方法,获得循环的下一个值,知道遇到 StopIteration 错误时退出循环:

     1 #!/usr/bin/python3
     2 
     3 class Fib(object):
     4     def __init__(self):
     5         self.a, self.b = 0, 1
     6 
     7     def __iter__(self):
     8         return self#实例本身就是迭代对象,故返回自己
     9 
    10     def __next__(self):
    11         self.a, self.b = self.b, self.a + self.b
    12         if self.a > 100:
    13             raise StopIteration();#引发一个异常
    14         return self.a
    15 
    16 for n in Fib():
    17     print(n)
    18 # 输出:
    19 # 1
    20 # 1
    21 # 2
    22 # 3
    23 # 5
    24 # 8
    25 # 13
    26 # 21
    27 # 34
    28 # 55
    29 # 89
    View Code

    __getitem__

    上面定义的 Fib 虽然能作用于 for 循环,和 list 有点像,但不能和 list 一样用下标取元素。要像 list 一样按照下标取出元素,需要实现 __getitem__() 方法:

     1 #!/usr/bin/python3
     2 
     3 class Fib(object):
     4     def __init__(self):
     5         self.a, self.b = 0, 1
     6 
     7     def __getitem__(self, n):
     8         a, b = 1, 1
     9         for x in range(n):
    10             a, b = b, a + b
    11         return a
    12 
    13     def __iter__(self):
    14         return self#实例本身就是迭代对象,故返回自己
    15 
    16     def __next__(self):
    17         self.a, self.b = self.b, self.a + self.b
    18         if self.a > 100:
    19             raise StopIteration();#引发一个异常
    20         return self.a
    21 
    22 fib = Fib()
    23 print(fib[3])#3
    24 # for i in fib:
    25 #     print(i)
    View Code

    __getattr__

    正常情况下,调用类的方法或属性时,如果类的方法或属性不存在就会报错。当实现了 __getattr__() 方法会动态返回一个属性.:

     1 class Studet(object):
     2     def __init__(self):
     3         self.name = 'xiao004'
     4 
     5     def __getattr__(self, attr):
     6         if attr == 'score':
     7             return 95
     8 
     9 st = Studet()
    10 print(st.score)#st并没有score属性,动态返回一个属性
    11 # 输出:
    12 # 95
    View Code

    当调用不存在的属性 score 时,python 解释器会调用 __getattr__(self,‘score’) 尝试获得属性。

    注意:只有在没有找到属性的情况下才调用__getattr__。此外,如果我们调用一个不存在且 __getattr__ 中也没处理的属性,仍然会报错

    __call__

    Python中的函数是一级对象。这意味着Python中的函数的引用可以作为输入传递到其他的函数/方法中,并在其中被执行。 
    而Python中类的实例(对象)可以被当做函数对待。也就是说,我们可以将它们作为输入传递到其他的函数/方法中并调用他们,正如我们调用一个正常的函数那样。而类中__call__()函数的意义正在于此。为了将一个类实例当做函数调用,我们需要在类中实现__call__()方法。也就是我们要在类中实现如下方法:def __call__(self, *args)。这个方法接受一定数量的变量作为输入。 
    假设x是X类的一个实例。那么调用x.__call__(1,2)等同于调用x(1,2)。这个实例本身在这里相当于一个函数。

    这段描述摘自:https://blog.csdn.net/yaokai_assultmaster/article/details/70256621

    即:实现了 __call__() 函数后能将实例本身当成一个函数使用

    代码:

     1 class Student(object):
     2     def __init__(self, name):
     3         self.name = name
     4 
     5     def __call__(self):
     6         print('名称:%s' % self.name)
     7 
     8 stu = Student('xiao004')
     9 stu()#实现了__call__()函数后能将实例本身当成一个函数使用
    10 # 输出:
    11 # 名称:xiao004
    View Code

    问题

    双下划线开头的实例变量一定不能从外部访问么?

    答:不是。不能从类外直接访问双下划线开头的变量是因为 python 解释器对外改变了双下划线开头的变量的名称

    如:

     1 class Student(object):
     2     def __init__(self, name, score):
     3         self.__name = name
     4         self.__score = score
     5 
     6     def info(self):
     7         print('学生:%s; 分是: %s' % (self.__name, self.__score))
     8 
     9 stu = Student('xiao004', 100)
    10 # print(stu.__score)#错误,__score是私有变量
    11 print(stu._Student__score)
    View Code

    在此列中不能直接访问 __score 是因为 python 解释器对外把 __score 变量改成了 _Student__score,所以我们能在类外用过 _Student__score 访问 __score 变量

  • 相关阅读:
    从内积的观点来看线性方程组
    《线性规划》(卢开澄,卢华明) 例2.1
    斐波那契数列
    共几只桃子
    计算 $s=1+(1+2)+(1+2+3)+cdots+(1+2+3+cdots+n)$
    【★】路由环路大总结!
    Apache与Tomcat有什么关系和区别
    Apache与Tomcat有什么关系和区别
    逻辑卷、物理卷、卷组 的关系
    逻辑卷、物理卷、卷组 的关系
  • 原文地址:https://www.cnblogs.com/geloutingyu/p/8833819.html
Copyright © 2011-2022 走看看