Python面向对象编程示例2019-07-21

您将了解Python如何实现面向对象编程(OOP)的抽象、数据封装、继承和多态概念。

面向对象编程是一种编程技术,它将程序组织成一组可重用的对象,这些对象之间相互作用,为给定的问题提供解决方案。一个程序就是是各种对象的集合,而各种对象是可重用的实体。一个对象可以是任何能够提供服务的实时实体。对象的例子有你,我,电话,汽车,风,银行帐户,销售员,发票等。


如何创建一个类及其对象?

类的定义以class关键字开头,后面跟一个类名,以冒号:结束。

示例

class Employee:
    salary = 10000
    name = "John Doe"
 
 
emp1 = Employee()
print(emp1.salary)
print(emp1.name)

输出:

10000
John Doe

如何创建一个空类

第二行种所使用的pass关键字表示没有数据属性和方法的一个空类。

示例

class Employee:
    pass
 
 
e1 = Employee()
print(e1)
 
e1.name = "John Doe"
print(e1.name)

输出:

<__main__.Employee object at 0x000001EEAEAB9400>
John Doe

如何使用关键字type创建一个类?

使用关键字type动态地创建一个新类,然后将其实例化。

示例:

e1 = type('Employee', (), {})()
print(e1)
 
e1.name = "John Doe"
print(e1.name)

输出:

<__main__.Employee object at 0x000001A4F4F09400>
John Doe

如何创建并调用类的方法?

类中的各种方法其实就是属于类的各种函数。

示例:

class Employee:
    salary = 10000
    name = "John Doe"
 
    def tax(self):
        print(self.salary * 0.10)
 
 
emp1 = Employee()
print(emp1.salary)
print(emp1.name)
emp1.tax()

输出:

10000
John Doe
1000.0

如何使用__init__()方法为数据属性赋值?

Python的类拥有一种被称为__init__的特殊方法,当在内存种创建某个类的一个实例的时候,它会自动执行。

示例:

class Employee:
        def __init__(self, salary, name):
                self.salary = salary
                self.name = name
 
 
emp1 = Employee(10000, "John Doe")
print(emp1.salary)
print(emp1.name)

输出:

10000
John Doe

如何更新类的各个属性(类中的各个变量)

示例:

class Employee:
    def __init__(self, salary, name):
        self.salary = salary
        self.name = name
 
 
emp1 = Employee(10000, "John Doe")
print(emp1.salary)
 
emp1.salary = 20000
print(emp1.salary)

输出

10000
20000

如何删除对象的属性和对象本身?

可以使用del关键字删除对象和对象的属性。

示例:

class Employee:
    def __init__(self, salary, name):
        self.salary = salary
        self.name = name
 
 
emp1 = Employee(10000, "John Doe")
 
del emp1.salary     # 删除对象的属性
del emp1            # 删除对象

如何检查和比较对象的类型

类或类型(type)是一个对象,它们包含着有关如何构造某种对象以及此类对象可以做什么的信息。一个type是一个类的类。与Python中的其他内容一样,类本身也是对象,您可以传递它们,将它们分配给变量等等。如果您要问一个类的类是什么,您得到答案将是type。如果你问一个类实例它的类是什么,你得到的答案当然会是这个类。

示例:

class Test:
    pass
 
 
print(type(Test))
 
obj1 = Test()
print(type(obj1))
 
obj2 = Test()
print(type(obj1) is type(obj2))

输出:



True

如何将一个对象的所有属性复制到另一个对象中?

示例:

class MyClass:
    def __init__(self):
        super(MyClass, self).__init__()
        self.foo = 1
        self.bar = 2
 
 
obj1 = MyClass()
obj2 = MyClass()
 
obj1.foo = 25
obj2.__dict__.update(obj1.__dict__)
 
print(obj1.foo)
print(obj2.foo)

输出:

25
25

How to Iterate over object attributes如何迭代对象的各个属性?

Calling dir on the object gives you back all the attributes of that object, including python special attributes.You can always filter out the special methods by using a list comprehension.
针对某个对象调用dir方法会返回该对象的所有属性和方法,包括Python自带的特殊属性和方法。您可以通过使用列表方式来过滤掉特殊属性与方法。

示例:

class A():
    m = 1
    n = 2
 
    def __int__(self, x=1, y=2, z=3):
        self.x = x
        self._y = y
        self.__z__ = z
 
    def xyz(self):
        print(x, y, z)
 
 
obj = A()
print(dir(obj))
print([a for a in dir(obj) if not a.startswith('__')])

输出:

['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__int__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'm', 'n', 'xyz']
['m', 'n', 'xyz']

打印某个对象的所有属性

针对某个对象调用vars方法会返回该对象object的属性和属性值的__dict__属性

示例:

class Animal(object):
    def __init__(self):
        self.eyes = 2
        self.name = 'Dog'
        self.color= 'Spotted'
        self.legs= 4
        self.age  = 10
        self.kids = 0
 
 
animal = Animal()
animal.tail = 1
 
temp = vars(animal)
for item in temp:
    print(item, ':', temp[item])

输出:

kids : 0
eyes : 2
name : Dog
color : Spotted
tail : 1
legs : 4
age : 10

如何动态创建类的数据属性?

针对某个对象调用setattr方法将给定对象已命名的各个属性设置成指定的值。

示例:

class Employee:
    pass
 
 
emp1 = Employee()
setattr(emp1, 'Salary', 12000)
 
emp2 = Employee()
setattr(emp2, 'Age', 25)
 
print(emp1.Salary)
print(emp2.Age)

输出:

12000
25

如何创建和使用自定义self参数?

self是对对象本身的一种对象引用,不是一定要把它命名为self(如下例中的person和emp参数,甚至这两个参数是可以等价互换的),但是,它必须是一个类的内部所有函数的第一个参数。

示例

class Employee:
    def __init__(person, salary, name):
        person.salary = salary
        person.name = name
 
    def print_details(emp):
        print(str(emp.salary) + ' : ' + emp.name)
 
 
emp1 = Employee(10000, 'John Doe')
emp1.print_details()

输出:

10000 : John Doe

如何使用self参数来维护对象的状态?

当对象被实例化时,对象本身被传递到self参数中。对象被传递到self参数中,这样对象就可以保留自己的数据。

示例:

class State(object):
    def __init__(self):
        self.field = 5.0
 
    def add(self, x):
        self.field += x
 
    def mul(self, x):
        self.field *= x
 
    def div(self, x):
        self.field /= x
 
    def sub(self, x):
        self.field -= x
 
 
s = State()
print(s.field)
 
s.add(2)        # self被隐式传递。
print(s.field)
 
s.mul(2)         # self被隐式传递。
print(s.field)
 
s.div(2)         # self被隐式传递。
print(s.field)
 
s.sub(2)          # self被隐式传递。
print(s.field)

输出

5.0
7.0
14.0
7.0
5.0

如何创建静态类变量?

在类定义的内部但不是在某个方法中所声明的变量就叫做类变量或静态变量。

示例:

class Employee:
    age = 25


print(Employee.age)

e = Employee()
print(e.age)

e.age = 30
emp = Employee()

print(Employee.age)  # 25
print(e.age)         # 30
print(emp.age)       # 25

输出:

25
25
25
30
25

创建多个类变量传入参数列表

class Employee(object):
    def __init__(self, **kwargs):
        for key in kwargs:
            setattr(self, key, kwargs[key])
 
 
emp = Employee(age=25, name="John Doe")
print(emp.age)
print(emp.name)

输出

25
John Doe

方法__init____call__有什么区别?

__init__应该被视为构造函数,而__call__方法可以被对象调用任意次数。但是__init____call__这两个函数都确实会使用默认参数。

示例:

class Counter:
    def __init__(self):
        self._weights = []
        for i in range(0, 2):
            self._weights.append(1)
        print(str(self._weights[-2]) + " No. from __init__")
 
    def __call__(self, t):
        self._weights = [self._weights[-1], self._weights[-1]
                         + self._weights[-1]]
        print(str(self._weights[-1]) + " No. from __call__")
 
 
num_count = Counter()
for i in range(0, 4):
    num_count(i)

输出:

1 No. from __init__
2 No. from __call__
4 No. from __call__
8 No. from __call__
16 No. from __call__

使用__init____new__方法的示例

当您需要控制创建一个新的实例的时时候,请使用__new__。当您需要控制初始化一个新实例的时候,请使用__init__

示例:

class Shape:
    def __new__(cls, sides, *args, **kwargs):
        if sides == 3:
            return Triangle(*args, **kwargs)
        else:
            return Square(*args, **kwargs)
 
 
class Triangle:
    def __init__(self, base, height):
        self.base = base
        self.height = height
 
    def area(self):
        return (self.base * self.height) / 2
 
 
class Square:
    def __init__(self, length):
        self.length = length
 
    def area(self):
        return self.length*self.length
 
 
a = Shape(sides=3, base=2, height=12)
b = Shape(sides=4, length=2)
 
print(str(a.__class__))
print(a.area())
 
print(str(b.__class__))
print(b.area())

输出:

class '__main__.Triangle'
12.0
class '__main__.Square'
4

迭代重载(Overloading)方法

__iter__方法返回迭代器对象,并在循环开始时隐式调用。__next__方法返回下一个值,并在每个循环增量中隐式调用。__ next__在没有更多要返回的值时会引发StopIteration异常,该异常由循环构造隐式捕获并停止迭代。

示例:

class Counter:
    def __init__(self, low, high):
        self.current = low
        self.high = high
 
    def __iter__(self):
        return self
 
    def __next__(self):
        if self.current > self.high:
            raise StopIteration
        else:
            self.current += 1
            return self.current - 1
 
 
for num in Counter(5, 15):
    print(num)

输出:

5
6
..
..
15

使用迭代器反转字符串

迭代器实现一个返回单个项的__Next__的方法以及一个返回self__iter__方法。

示例:

class Reverse:
    def __init__(self, data):
        self.data = data
        self.index = len(data)
 
    def __iter__(self):
        return self
 
    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]
 
 
test = Reverse('Python')
for char in test:
    print(char)

输出:

n
o
h
t
y
P

使用__reversed__魔术方法示例

迭代可以让__reversed__方法返回一个向后的迭代器。

示例:

class Count:
    def __init__(self, start, end):
        self.start = start
        self.end = end
        self.current = None
 
    def __iter__(self):
        self.current = self.start
        while self.current < self.end:
            yield self.current
            self.current += 1
 
    def __next__(self):
        if self.current is None:
            self.current = self.start
        if self.current > self.end:
            raise StopIteration
        else:
            self.current += 1
            return self.current-1
 
    def __reversed__(self):
        self.current = self.end
        while self.current >= self.start:
            yield self.current
            self.current -= 1
 
 
obj1 = Count(0, 5)
for i in obj1:
    print(i)
 
obj2 = reversed(obj1)
for i in obj2:
    print(i)

输出:

0
1
2
3
4
5
4
3
2
1
0

使用__getitem____setitem__方法进行索引和切片

示例:

class Counter(object):
    def __init__(self, floors):
        self._floors = [None]*floors
 
    def __setitem__(self, floor_number, data):
        self._floors[floor_number] = data
 
    def __getitem__(self, floor_number):
        return self._floors[floor_number]
 
 
index = Counter(4)
index[0] = 'ABCD'
index[1] = 'EFGH'
index[2] = 'IJKL'
index[3] = 'MNOP'
 
print(index[2])

输出:

IJKL

使用__getattr____setattr__分配属性

__getattr_方法会拦截属性的引用,而__setattr__方法会拦截所有属性的分配。

实例:

class Employee(object):
    def __init__(self, data):
        super().__setattr__('data', dict())
        self.data = data
 
    def __getattr__(self, name):
        if name in self.data:
            return self.data[name]
        else:
            return 0
 
    def __setattr__(self, key, value):
        if key in self.data:
            self.data[key] = value
        else:
            super().__setattr__(key, value)
 
 
emp = Employee({'age': 23, 'name': 'John'})
print(emp.age)
print(emp.name)
print(emp.data)
print(emp.salary)
 
emp.salary = 50000
print(emp.salary)

输出

23
John
{'age': 23, 'name': 'John'}
0
50000

什么是__del__方法以及如何调用它?

__del__方法是一个终结器。当一个对象被垃圾收集时(在删除对该对象的所有引用之后的某个时刻发生),就会调用它。

示例:

class Employee():
    def __init__(self, name='John Doe'):
        print('Hello ' + name)
        self.name = name
 
    def developer(self):
        print(self.name)
 
    def __del__(self):
        print('Good Bye ' + self.name)
 
 
emp = Employee('Mark')
print(emp)
 
emp = 'Rocky'
print(emp)

输出:

Hello Mark
<__main__.Employee object at 0x00000000012498D0>
Good Bye Mark
Rocky

如何创建类的私有成员?

如果Python的一个函数、类方法或属性的名称是以两个下划线开头的(但不以两个下划线结尾),那么它是私有的;其他所有内容都是公共的。

示例

class Test(object):
    __private_var = 100
    public_var = 200
 
    def __private_func(self):
        print('Private Function')
 
    def public_func(self):
        print('Public Function')
        print(self.public_var)
 
    def call_private(self):
        self.__private_func()
        print(self.__private_var)
 
 
t = Test()
print(t.call_private())
print(t.public_func())

输出

Private Function
100
None
Public Function
200
None

举一个用Python封装的例子

将数据和函数包装成一个单元(称为类)就被称为封装。数据封装是一个类最显著的特性。外部世界无法访问数据,只有包裹在类中的函数才能访问它。这些函数提供了对象数据和程序之间的接口。这种数据与程序直接访问的隔离被称为数据隐藏或信息隐藏。

示例:

class Encapsulation:
    __name = None
 
    def __init__(self, name):
        self.__name = name
 
    def get_name(self):
        return self.__name
 
 
pobj = Encapsulation('Rocky')
print(pobj.get_name())

输出:

Rocky

举一个Python中合成的例子

在合成中,一个类由其他类的一个或多个实例组成。换句话说,一个类是容器,另一个类是内容,如果删除容器对象,那么它的所有内容对象也将被删除。

示例:

class Salary:
    def __init__(self, pay):
        self.pay = pay
 
    def get_total(self):
        return self.pay * 12
 
 
class Employee:
    def __init__(self, pay, bonus):
        self.pay = pay
        self.bonus = bonus
        self.obj_salary = Salary(self.pay)
 
    def annual_salary(self):
        return "Total: " + str(self.obj_salary.get_total() + self.bonus)
 
 
obj_emp = Employee(600, 500)
print(obj_emp.annual_salary())

输出:

Total: 7700

举一个Python中聚合的例子

聚合是合成的一种弱形式。如果删除容器对象内容,则对象可以在没有容器对象的情况下存活。

示例:

class Salary:
    def __init__(self, pay):
        self.pay = pay
 
    def get_total(self):
        return (self.pay*12)
 
 
class Employee:
    def __init__(self, pay, bonus):
        self.pay = pay
        self.bonus = bonus
 
    def annual_salary(self):
        return "Total: " + str(self.pay.get_total() + self.bonus)
 
 
obj_sal = Salary(600)
obj_emp = Employee(obj_sal, 500)
print(obj_emp.annual_salary())

输出:

Total: 7700

单继承、多继承和多级继承

示例:

# 单继承
class Apple:
    manufacturer = 'Apple Inc'
    contact_website = 'www.apple.com/contact'
    name = 'Apple'
 
    def contact_details(self):
        print('Contact us at ', self.contact_website)
 
 
class MacBook(Apple):
    def __init__(self):
        self.year_of_manufacture = 2018
 
    def manufacture_details(self):
        print('This MacBook was manufactured in {0}, by {1}.'
              .format(self.year_of_manufacture, self.manufacturer))
 
 
macbook = MacBook()
macbook.manufacture_details()
 
 
# 多继承
class OperatingSystem:
    multitasking = True
    name = 'Mac OS'
 
 
class MacTower(OperatingSystem, Apple):
    def __init__(self):
        if self.multitasking is True:
            print('Multitasking system')
        # 如果有两个属性名相同的超级类,
        # 则调用第一个继承的超级类的属性,
        # 继承顺序很重要。
        # 由于在MacTower这个子类中所继承的OperationSystem类
        # 在前面(即顺序为从左向右数),所以下面的self.name
        # 指的是继承自OperationSystem类的name属性
        # 而不是Apple类的name属性
        print('Name: {}'.format(self.name))
 
 
mactower = MacTower()
 
 
# 多级继承
class MusicalInstrument:
    num_of_major_keys = 12
 
 
class StringInstrument(MusicalInstrument):
    type_of_wood = 'Tonewood'
 
 
class Guitar(StringInstrument):
    def __init__(self):
        self.num_of_strings = 6
        print("The guitar consists of {0} strings, \
it is made of {1} and can make {2} keys."
              .format(self.num_of_strings,
                      self.type_of_wood, self.num_of_major_keys))
 
 
guitar = Guitar()

输出:

This MacBook was manufactured in 2018, by Apple Inc.
Multitasking system
Name: Mac OS
The guitar consists of 6 strings, it is made of Tonewood and can play 12 keys.

如何获得某个类的各个父类?

用子类的__bases__方法

示例:

class A(object):
    pass
 
 
class B(object):
    pass
 
 
class C(A, B):
    pass
 
 
print(C.__bases__)

输出:

(< class '__main__.A' >, < class '__main__.B' >)

多态

多态允许我们在子类中定义与父类中某个方法同名的方法。多态仅仅指方法,不包括属性

示例:

# 创建一个名叫Shape的类
class Shape:
    width = 0
    height = 0
 
    # 创建一个名叫area的方法
    def area(self):
        print("Parent class's 'area' method... ")
 
 
# 创建父类Shape类的一个名叫Rectangle的子类
class Rectangle(Shape):
 
    def __init__(self, w, h):
        self.width = w
        self.height = h
 
    # 重写area方法
    def area(self):
        print("Area of the Rectangle is : ", self.width * self.height)
 
 
# 创建父类Shape类的一个名叫Triangle的子类
class Triangle(Shape):
 
    def __init__(self, w, h):
        self.width = w
        self.height = h
 
    # 重写area方法
    def area(self):
        print("Area of the Triangle is : ", (self.width * self.height) / 2)
 
 
shape = Shape()
rectangle = Rectangle(10, 20)
triangle = Triangle(2, 10)

shape.area()
rectangle.area()
triangle.area()

输出:

Parent class's method 'area'......
The area of the rectangle is: 200
The area of the rectangle is: 10.0

如何在子类中访问父类的私有成员?

示例:

class Human():
 
    # 私有变量 var
    __privateVar = "this is __private variable"
 
    # 构造器方法
    def __init__(self):
        self.className = "Human class constructor"
        self.__privateVar = "this is redefined __private variable"
 
    # 公有方法
    def showName(self, name):
        self.name = name
        return self.__privateVar + " " + name
 
    # 私有方法
    def __privateMethod(self):
        return "Private method"
 
    # 返回一个私有变量的公用方法
    def showPrivate(self):
        return self.__privateMethod()
 
    def showProtecded(self):
        return self._protectedMethod()
 
 
class Male(Human):
    def showClassName(self):
        return "Male"
 
    def showPrivate(self):
        return self.__privateMethod()
 
    def showProtected(self):
        return self._protectedMethod()
 
 
class Female(Human):
    def showClassName(self):
        return "Female"
 
    def showPrivate(self):
        return self.__privateMethod()
 
 
human = Human()
print(human.className)
print(human.showName("Vasya"))
print(human.showPrivate())
 
male = Male()
print(male.className)
print(male.showClassName())
 
female = Female()
print(female.className)
print(female.showClassName())

输出:

Human class constructor
this is redefined __private variable Vasya
Private method
Human class constructor
Male
Human class constructor
Female

什么是一个抽象类?

抽象类是那些包含着一个或多个抽象方法的类。一个抽象方法是已经声明了,但不包含任何实现过程的方法。抽象类不能被实例化,并且需要该抽象类的子类为这个抽象方法提供实现过程。对于抽象类与其子类,Python有如下的规定:

  • 所谓抽象类是指首先使用语句from abc import ABC, abstractmethod导入abc模块的ABC方法和abstractmethod方法后,将ABC作为该抽象类的父类,同时在要定义的抽象方法(如示例抽象类AbstractClass内部定义的eat方法)上面增加一条@abstractmethod语句,才是所谓的抽象类。这个@abstractmethod语句就是一个装饰器,将其下面的方法设置(或装饰)为一个抽象方法。换句话说,如果想要把抽象类中的任意一个方法变成抽象的,就在这个方法的上面添加一条@abstractmethod语句。甚至可以把构造器方法(即__init__方法)也变成抽象方法,那么就可以在实现抽象类的子类中重写默认源于父抽象类中的构造方法(见下一个示例)。
  • 在实现抽象类的子类(如示例中的Parents类和Babies类)的内部,就必须要定义一个与子类的父抽象类内部的抽象方法名字一样的一个方法,即示例中的eat方法,而方法的内容可以是任意内容。
  • 如果抽象类中用@abstractmethod语句装饰出多个抽象方法,那么在其子类中就必须定义出相同数量(参见示例注释中的test方法)的,且相同名称的方法。

示例:

from abc import ABC, abstractmethod


class AbstractClass(ABC):
    def __init__(self, value):
        self.value = value
        super().__init__()

    @abstractmethod
    def eat(self):
        pass

    # @abstractmethod
    # def test(self):
    #     pass


class Parents(AbstractClass):
    def eat(self):
        return "Eat solid food " + str(self.value) + " times each day."

    # def test(self):
    #     return str(self.value) + " times testing"


class Babies(AbstractClass):
    def eat(self):
        return "Milk only " + str(self.value) + " times or more each day"


food = 3
adult = Parents(food)
print("Adult")
print(adult.eat())
# print(adult.test())

infant = Babies(food)
print("Infants")
print(infant.eat())

输出:

Adult
Eat solid food 3 times each day.
Infants
Milk only 3 times or more each day.

创建一个抽象类来重写继承子类中来自于抽象父类的默认构造函数

示例:

from abc import ABCMeta, abstractmethod
 
 
class AbstractClass(object, metaclass=ABCMeta):
    @abstractmethod
    def __init__(self, n):
        self.n = n
 
 
class Employee(AbstractClass):
    def __init__(self, salary, name):
        self.salary = salary
        self.name = name
 
 
emp1 = Employee(10000, "John Doe")
print(emp1.salary)
print(emp1.name)

输出:

10000
John Doe

使抽象类继承另一个抽象类

从某个抽象类派生出来的一个类是无法被实例化的,除非重写其所有的抽象方法,不论自己的抽象方法,还是从父抽象类中派生出来的抽象方法。

示例:

from abc import ABC, abstractmethod
 
 
class A(ABC):
    def __init__(self, username):
        self.username = username
        super().__init__()
 
    @abstractmethod
    def name(self):
        pass
 
 
class B(A):
    @abstractmethod
    def age(self):
        pass
 
 
class C(B):
    def name(self):
        print(self.username)
 
    def age(self):
        return
 
 
c = C('Test1234')
c.name()

输出:

Test1234

Super在Python中是干什么的?

在具有单一继承的类层次结构中,可以使用super引用父类,而不显式命名它们,从而使代码更易于维护。这种用法与在其他编程语言中使用super非常相似。

示例:

class A(object):
    def __init__(self, profession):
        print(profession)
 
 
class B(A):
    def __init__(self):
        print('John Doe')
        super().__init__('Developer')
 
 
b = B()

输出:

John Doe
Developer

在多重继承中,super()是如何与__init__()方法一起工作的?

在一个动态执行环境中,super支持与多重继承的协同工作。这种方法是Python所独有的,在静态编译语言或只支持单一继承的语言中是找不到的。

示例:

class F:
    def __init__(self):
        print('F%s' % super().__init__)
        super().__init__()
 
 
class G:
    def __init__(self):
        print('G%s' % super().__init__)
        super().__init__()
 
 
class H:
    def __init__(self):
        print('H%s' % super().__init__)
        super().__init__()
 
 
class E(G, H):
    def __init__(self):
        print('E%s' % super().__init__)
        super().__init__()
 
 
class D(E, F):
    def __init__(self):
        print('D%s' % super().__init__)
        super().__init__()
 
 
class C(E, G):
    def __init__(self):
        print('C%s' % super().__init__)
        super().__init__()
 
 
class B(C, H):
    def __init__(self):
        print('B%s' % super().__init__)
        super().__init__()
 
 
class A(D, B, E):
    def __init__(self):
        print('A%s' % super().__init__)
        super().__init__()
 
 
a = A()
print(a)

输出:

A bound method D.__init__ of __main__.A object at 0x000000000369CFD0
D bound method B.__init__ of __main__.A object at 0x000000000369CFD0
B bound method C.__init__ of __main__.A object at 0x000000000369CFD0
C bound method E.__init__ of __main__.A object at 0x000000000369CFD0
E bound method G.__init__ of __main__.A object at 0x000000000369CFD0
G bound method H.__init__ of __main__.A object at 0x000000000369CFD0
H bound method F.__init__ of __main__.A object at 0x000000000369CFD0
F method-wrapper '__init__' of A object at 0x000000000369CFD0
__main__.A object at 0x000000000369CFD0

如何使用带有类方法的super

当调用super方法将某个类的方法、实例方法或静态方法解析为父级版本时,我们希望把我们当前所在的类的作用域传递成为第一个参数,同时指明我们所要解析的是哪个父级作用域,以及作为我们感兴趣的对象的第二个参数,还要指明我们要将该作用域应用到哪个对象上。

示例:

class A:
    @classmethod
    def name(cls, employee):
        print("Employee Name: ", employee)


class B(A):
    @classmethod
    def name(cls, employee):
        super(B, cls).name(employee)


B.name("John Doe")

输出:

Employee Name:  John Doe

mro是做什么的?

mro表示方法解析顺序(Method Resolution Order)。它返回某个派生类来源的类型列表,表示他们搜索方法的顺序。

示例:

class A(object):
    def dothis(self):
        print('From A class')
 
 
class B1(A):
    def dothis(self):
        print('From B1 class')
    pass
 
 
class B2(object):
    def dothis(self):
        print('From B2 class')
    pass
 
 
class B3(A):
    def dothis(self):
        print('From B3 class')
 
 
# 多米诺继承
class D1(B1, B3):
    pass
 
 
class D2(B1, B2):
    pass
 
 
d1_instance = D1()
d1_instance.dothis()
print(D1.__mro__)
 
d2_instance = D2()
d2_instance.dothis()
print(D2.__mro__)

输出:

From B1 class
(class '__main__.D1', class '__main__.B1', )
From B1 class
(class '__main__.D2', class '__main__.B1', , class '__main__.B2', class 'object')

是什么Python中的元类(metaclasses)?

元类是所有类的类。一个类所定义的是这个类的某个实例(即对象)的行为,而元类定义的是某个类的行为。一个类就是元类的实例。元类最常被用作为类工厂。您通过调用某个类来创建一个对象,而Python通过调用元类来创建一个新的类(当它执行“class”语句的时候)。然而,元类实际上定义的是某一个类的类型,而不仅仅是它的一个工厂,所以您可以用它们做更多的事情。

示例:

def _addMethod(fldName, clsName, verb, methodMaker, dict):
    compiledName = _getCompiledName(fldName, clsName)
    methodName = _getMethodName(fldName, verb)
    dict[methodName] = methodMaker(compiledName)
 
 
def _getCompiledName(fldName, clsName):
    if fldName[:2] == "__" and fldName[-2:] != "__":
        return "_%s%s" % (clsName, fldName)
    else:
        return fldName
 
 
def _getMethodName(fldName, verb):
    s = fldName.lstrip("_")
    return verb + s.capitalize()
 
 
def _makeGetter(compiledName):
    return lambda self: self.__dict__[compiledName]
 
 
def _makeSetter(compiledName):
    return lambda self, value: setattr(self, compiledName, value)
 
 
class Accessors(type):
    def __new__(cls, clsName, bases, dict):
        for fldName in dict.get("_READ", []) + dict.get("_READ_WRITE", []):
            _addMethod(fldName, clsName, "get", _makeGetter, dict)
        for fldName in dict.get("_WRITE", []) + dict.get("_READ_WRITE", []):
            _addMethod(fldName, clsName, "set", _makeSetter, dict)
        return type.__new__(cls, clsName, bases, dict)
 
 
class Employee(object, metaclass=Accessors):
    _READ_WRITE = ['name', 'salary', 'title', 'bonus']
 
    def __init__(self, name, salary, title, bonus=0):
        self.name = name
        self.salary = salary
        self.title = title
        self.bonus = bonus
 
 
b = Employee('John Doe', 25000, 'Developer', 5000)
print('Name:', b.getName())
print('Salary:', b.getSalary())
print('Title:', b.getTitle())
print('Bonus:', b.getBonus())

输出:

Name: John Doe
Salary: 25000
Title: Developer
Bonus: 5000

元类的具体用例是什么?

metaclass允许您将类的功能与创建它的详细信息分开。元类和类都只各自负责一件事情。您可以在一个元类中只编写一次代码,而用它来定制多个类的调用行为,但是不必担心有多个继承的存在。子类可以使用它们的__new__方法来重写它们的行为,但是对元类使用__call__方法甚至不必调用__new__方法。如果需要进行设置工作,您可以使用元类中的__new__方法进行,而且只需要使用一次,而不是每次都要调用这个类。

示例:

class UpperAttrNameMetaClass(type):
        def __new__(cls, clsname, bases, attrdict, *args, **kwargs):
                print('1. Create a new type, from ' +
                      ' UpperAttrNameMetaClass.__new__')
                new_attrs = dict()
                for attr, value in attrdict.items():
                        if not callable(value) and not str(attr).startswith('__'):
                                new_attrs[attr.upper()] = value
                        else:
                                new_attrs[attr] = value
 
                cls_obj = super().__new__(cls, clsname, bases, new_attrs,
                                          *args, **kwargs)
                return cls_obj
 
        def __init__(self, clsname, bases, attrdict):
                self.test = 'test'
                super().__init__(clsname, bases, attrdict)
                print('2. Initialize new type, increase test attribute,' +
                      'from UpperAttrNameMetaClass.__init__')
 
        def __call__(self, *args, **kwargs):
                print('3. Instantiate the new class,' +
                      ' from UpperAttrNameMetaClass.__call__')
                new_obj = self.__new__(self, *args, **kwargs)
                new_obj.__init__(*args, **kwargs)
                return new_obj
 
 
class ObjectNoInitMetaClass(type):
        def __call__(cls, *args, **kwargs):
                if len(args):
                        raise TypeError('Must use keyword argument ' +
                                        ' for key function')
                new_obj = cls.__new__(cls)
                for k, v in kwargs.items():
                        setattr(new_obj, k.upper(), v)
                return new_obj
 
 
class Pig(object, metaclass=UpperAttrNameMetaClass):
        size = 'Big'
 
        def __new__(cls, *args, **kwargs):
                print('4. Call __new__ in the __call__ of the metaclass,' +
                      ' from Pig.__new__')
                obj = object.__new__(cls)
                return obj
 
        def __init__(self):
                print('5. After the new object is instantiated in ' +
                      'the __call__ of the metaclass,the object is promoted,' +
                      ' from Pig.__init__')
                self.name = 'Mark'
 
        def talk(self):
                print(self.name)
 
 
Pig().talk()
print(Pig.__dict__)
print(Pig.SIZE)
 
 
class AnyOne(metaclass=ObjectNoInitMetaClass):
        pass
 
 
foo = AnyOne(name='John', age=28)
print(foo.NAME, foo.AGE)
print(foo.__dict__)

输出:

1. Create a new type, from  UpperAttrNameMetaClass.__new__
2. Initialize new type, increase test attribute,from UpperAttrNameMetaClass.__init__
3. Instantiate the new class, from UpperAttrNameMetaClass.__call__
4. Call __new__ in the __call__ of the metaclass, from Pig.__new__
5. After the new object is instantiated in the __call__ of the metaclass,the object is promoted, from Pig.__init__
Mark
{'__doc__': None, 'test': 'test', '__weakref__': , 'SIZE': 'Big', '__init__': , '__dict__': , '__module__': '__main__', '__new__': , 'talk': }
Big
John 28
{'AGE': 28, 'NAME': 'John'}

使用元类的单例类

元类是用于创建类的类。元类通常是type class的子类,它重新定义类创建协议方法,以便在类语句末尾发出的对类创建的调用进行自定义。

示例:

class SingleInstanceMetaClass(type):
    def __init__(self, name, bases, dic):
        self.__single_instance = None
        super().__init__(name, bases, dic)
 
    def __call__(cls, *args, **kwargs):
        if cls.__single_instance:
            return cls.__single_instance
        single_obj = cls.__new__(cls)
        single_obj.__init__(*args, **kwargs)
        cls.__single_instance = single_obj
        return single_obj
 
 
class Setting(metaclass=SingleInstanceMetaClass):
    def __init__(self):
        self.db = 'MySQL'
        self.port = 3306
 
 
bar1 = Setting()
bar2 = Setting()
 
print(bar1 is bar2)
print(bar1.db, bar1.port)
bar1.db = 'ORACLE'
print(bar2.db, bar2.port)

输出:

True
MySQL 3306
ORACLE 3306

@staticmethod和@classmethod之间有什么区别?

@staticmethod 函数只不过是某个类的内部所定义的一个函数。它不需要先对类进行实例化就可以被调用。通过继承所形成的定义是不变的。@Classmethod函数也可以在不对类进行实例化的情况下被调用,但它是根据子类进行定义的,而不是通过继承父类进行定义的。

示例:

class Employee:
    @classmethod
    def classmthd(*args):
        return args
 
    @staticmethod
    def staticmthd(*args):
        return args
 
 
print(Employee.classmthd())
print(Employee.classmthd('test'))
 
print(Employee.staticmthd())
print(Employee.staticmthd('test'))

输出:

(class '__main__.Employee',)
(class '__main__.Employee', 'test')
()
('test',)

什么是Python的装饰器(decorator)?

Python的装饰器只不过是一些简单的函数或其他可调用的内容,它们将要被装饰的函数或类变成一个参数。因此,您可以使用许多普通的函数作为装饰器,反之亦然。

示例:

def message(param1, param2):
    def wrapper(wrapped):
        class WrappedClass(wrapped):
            def __init__(self):
                self.param1 = param1
                self.param2 = param2
                super(WrappedClass, self).__init__()
 
            def get_message(self):
                return "message %s %s" % (self.param1, self.param2)
 
        return WrappedClass
    return wrapper
 
 
@message("param1", "param2")
class Pizza(object):
    def __init__(self):
        pass
 
 
pizza_with_message = Pizza()
print(pizza_with_message.get_message())

输出:

message param1 param2

如何制作一个多个函数装饰器的链

修饰器具有非常明确的语法,这使得它们比帮助函数调用更容易被发现,而帮助函数调用可能被任意地从主题函数或类中删除。在代码维护和一致性方面,装饰器具有一些优势。

示例:

def benchmark(func):
    """
    一个记录某个函数运行所需要的时间的装饰器
    """
    import time

    def wrapper(*args, **kwargs):
        t = time.process_time()
        res = func(*args, **kwargs)
        print("{0} {1}".format(func.__name__, time.process_time() - t))
        return res
    return wrapper

def logging(func):
    """
    一个记录脚本活动的装饰器。
    (实际上,它只是打印出来,但它可能正在进行记录!)
    """
    def wrapper(*args, **kwargs):
        res = func(*args, **kwargs)
        print("{0} {1} {2}".format(func.__name__, args, kwargs))
        return res
    return wrapper

def counter(func):
    """
    一个记录并打印某个函数运行次数的装饰器
    """
    def wrapper(*args, **kwargs):
        wrapper.count = wrapper.count + 1
        res = func(*args, **kwargs)
        print("{0} has been usd: {1}x".format(func.__name__, wrapper.count))
        return res
    wrapper.count = 0
    return wrapper


@counter
@benchmark
@logging
def letter_range(start, stop, step=1):
    start = ord(start.lower())
    stop = ord(stop.lower())
    for str_1st in range(start, stop, step):
        yield chr(str_1st)


print(list(letter_range("a", "f")))
print("\n")
print(list(letter_range("m", "z", 2)))

输出:

letter_range ('a', 'f') {}
wrapper 0.0009437184107374183
wrapper has been used: 1x
['a', 'b', 'c', 'd', 'e']


letter_range ('m', 'z', 2) {}
wrapper 3.131164480070134e-05
wrapper has been used: 2x
['m', 'o', 'q', 's', 'u', 'w', 'y']

类方法和函数都使用的装饰器

函数装饰器也可以用来管理函数对象,而类装饰器也可以用来直接管理类对象。

示例:

def deco(func):
    def inner(*args):
        print('Decorator: args={}'.format(args))
        func(*args)
    return inner
 
 
class Class:
    @deco
    def class_method(self, param):
        print('Parameter is {}'.format(param))
 
 
@deco
def static_function(a, b, c):
    print('{} {} {}'.format(a, b, c))
 
 
Class().class_method(25000)
static_function('Australia', 'Germany', 'London')

输出:

Decorator: args=(<__main__.Class object at 0x0000000001262278>, 25000)
Parameter is 25000
Decorator: args=('Australia', 'Germany', 'London')
Australia Germany London

创建带参数的装饰器

函数和类修饰符似乎也可以接受参数,尽管实际上这些参数被传递给实际返回装饰器的可调用对象,而装饰器又返回可调用对象。

示例:

class Profile(object):
    def __init__(self, flag):
        self.flag = flag
 
    def __call__(self, original_func):
        decorator_self = self
 
        def wrappee(*args, **kwargs):
            print('Skills Before Joining: ', decorator_self.flag)
            original_func(*args, **kwargs)
            print('Skills After Joining: ', decorator_self.flag)
        return wrappee
 
 
@Profile('Php, Java, Python, Go')
def employee(name, age):
    print('Employee: ', name, age)
 
 
employee('John Doe', 28)

输出:

Skills Before Joining:  Php, Java, Python, Go
Employee:  John Doe 28
Skills After Joining:  Php, Java, Python, Go

你可能感兴趣的:(Python面向对象编程示例2019-07-21)