已复制
全屏展示
复制代码

Python面向对象从入门到精通


· 22 min read

面向对象中的类和实例

  • 是众多具有相似属性实例的一种抽象,比如老师、学生这是两个类,类是指抽象出的模板。
  • 实例 是类的一个个具体体现,比如我们学校有郑老师、张老师, 有小张同学、小余同学,实例是根据类创建出来的具体的对象。每个对象都拥有从类中继承的相同的方法,但各自的数据互相独立,互不影响。

定义类和实例

class Student(object):

    def __init__(self, name, home='beijing'):
        self.name = name
        self.home = home

    def __str__(self):
        return self.name

s1 = Student('yuziyue')
s2 = Student('yuchaoshui')
print(s1)
print(s2)

首先定义了一个学生的类。__init__() 是初始化函数,初始化定义了namehome__str__()是类的特殊函数,打印该类的对象时会调用该函数。然后根据 Student 对象实例化两个对象 s1s2,并打印出s1s2

初始化函数__init__

初始化函数的意思是在对象定义的时候,给该对象属性的值初始化。

class Student(object):

    def __init__(self, name, home='beijing'):
        self.name = name
        self.home = home

    def __str__(self):
        return self.name


class Grad(Student):

    def __init__(self, name, home='beijing', title='teacher'):
        super().__init__(name, home)
        self.title = title

    def __str__(self):
        return self.name

s = Student('zhanghong')
g = Grad('xiaowang')
print(s)
print(g)
print(g.title)

现在定义了两个类StudentGrad,Grad从Student继承而来,两个类分别都有自己的初始化函数__init__(), 如果子类希望继承父类的属性,就必须使用 super() 函数调用父类的初始化函数,也可以像这样调用父类 super(Grad, self).__init__(name, home)注意:不推荐使用 类名.__init__() 这种方式调用初始化函数,后期可能会出现问题。

构造函数__new__

__new__方法接受的参数和__init__一样。 __init__是在类实例创建之后调用,用作初始化对象的属性。而 __new__ 方法正是创建这个类实例的方法。调用顺序是先调用__new__(),然后调用__init__()

  • 1.__init__ 通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作,发生在类实例被创建完以后。它是实例级别的方法。
  • 2.__new__ 通常用于控制生成一个新实例的过程。它是类级别的方法。
class Person(object):
    def __new__(cls, name, age):
        print('__new__ called.')
        return super(Person, cls).__new__(cls)

    def __init__(self, name, age):
        print('__init__ called.')
        self.name = name
        self.age = age

    def __str__(self):
        return 'instance: ' + self.name

if __name__ == '__main__':
    yuchaoshui = Person('yuchaoshui', 24)
    print(yuchaoshui)
    print(yuchaoshui.name)
    print(yuchaoshui.age)


# 结果如下:
__new__ called.
__init__ called.
instance: yuchaoshui
yuchaoshui
24

下面是改进测试版,把 __init__()函数给去掉了同样可以初始化对象。

class Person(object):
    def __new__(cls, name, age):
        print('__new__ called.')
        instance = super(Person, cls).__new__(cls)
        instance.name = name
        instance.age = age

        return instance

    def __str__(self):
        return 'instance: ' + self.name

if __name__ == '__main__':
    yuchaoshui = Person('yuchaoshui', 24)
    print(yuchaoshui)
    print(yuchaoshui.name)
    print(yuchaoshui.age)


# 结果如下
__new__ called.
instance: yuchaoshui
yuchaoshui
24

类和对象的属性和方法

实例属性

实例属性就是与实例绑定的属性,可以用 实例名.属性名 的方式调用,它代表了一个实例的数据部分。

class Student(object):

    def __init__(self, name, home='beijing'):
        self.name = name

    def __str__(self):
        return self.name

s = Student('yuziyue')
print(s.name)

类属性

类属性是类的属性,当定义了一个类属性后,这个属性虽然归类所有,但类和实例都可以访问到,类属性是类和类所有实例的共有数据。当实例属性和类属性同名时,访问顺序是:实例属性名--》类属性名,如果都不存在则报错。

class Student(object):

    school = 'beijing university'

    def __init__(self, name, home='beijing'):
        self.name = name

    def __str__(self):
        return self.name

s = Student('yuziyue')
print(s.name)
print(s.school)
print(Student.school)

实例方法

实例方法就是与实例绑定的函数,可以用 实例名.方法名的方式调用,它的第一个参数表示实例本身,通常用 self 来表示,实例方法可以直接访问实例的数据做处理。通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。

class Student(object):

    def __init__(self, name, home='beijing'):
        self.name = name

    def __str__(self):
        return self.name

    def gotoclass(self):
        act = "{} is going to class!".format(self.name)
        print(act)

s = Student('yuziyue')
s.gotoclass()

类方法

类方法用于访问类属性,用@classmethod装饰器来装饰,第一个参数是 clscls 用于调用类属性,但不能访问实例属性!

使用场景1:由于python类中只能有一个初始化方法,不能按照不同的情况初始化类。使用类方法可以定义多个初始化函数的情况。

使用场景2:用于调用静态方法,使得类方法的代码更简单。

静态方法也可以实现定义多个初始化函数的功能,但静态方法每次都要写上类的名字,不方便,同时防止类名修改后,需要在方法定义中修改原来的类名称。

class Student(object):

    def __init__(self, name, home='beijing'):
        self.name = name

    def __str__(self):
        return self.name

    @classmethod
    def create(cls, name):
        student = cls(name)
        return student

s1 = Student('yuziyue')
s2 = Student.create('yuchaoshui')
print(s1.name)
print(s2.name)

静态方法

静态方法用 @classmethod 装饰,方法内不能访问实例属性,参数不能传入 selfcls, 调用时可以用类或者实例调用。使用场景: 当类中某个方法不依赖实例时,避免传入 self 实例消耗资源成本(内存等)。

class Student(object):

    school = "beijing university"

    def __init__(self, name, home='beijing'):
        self.name = name

    def __str__(self):
        return self.name

    @staticmethod
    def get_school():
        school = Student.school
        return school

s = Student('yuziyue')
print(s.get_school())
print(Student.get_school())

访问限制

私有属性和方法

  • __ (双下划线) 开头的属性或者方法是私有属性或方法,只能类内部访问,不能被实例访问,不能被子类继承。对于对象来说,双下划线的私有属性或方法的名称变了,变成了 _classname__privartename, 比如 s._Student__mom 可以通过这样的方式访问到数据,但是这是极力不允许的。
  • _ (单下划线)开头的属性或者方法也是私有属性或方法,但是它能像公有属性和方法一样可以被类内方法、对象访问,,但是我们通常不去这么做,这样做很危险。也可以被子类继承下去。
  • 开头结尾都是双下划线的属性或方式是类的特殊成员,有特殊用途,比如__init__(self)
  • 单下划线和双下划线来命名私有属性, 到底哪种方式好呢? 大多数而言,应该让非公共名称以单下划线开头。但是,如果你清楚你的代码会涉及到子类, 并且有些内部属性应该在子类中隐藏起来,那么才考虑使用双下划线方案。
  • 有时候定义的一个变量和某个保留关键字冲突,这时候可以使用单下划线作为后缀,例如:lambda_ = 3
class Student(object):

    def __init__(self, name, phone='', mom='', dad='', home='beijing'):
        self.name = name
        self.__mom = mom
        self.__dad = dad
        self._phone = phone
        self._home = home

    def __str__(self):
        return self.name

    def __bed(self):
        bedstr = "{} is sleepping!".format(self.name)
        print(bedstr)

    def __info(self):
        print("mom: {}".format(self.__mom))
        print("dad: {}".format(self.__dad))
        print("phone: {}".format(self._phone))
        print("home: {}".format(self._home))

    def _meeting(self):
        meetingstr = "{} is in a meeting!".format(self.name)
        print(meetingstr)

    def get_status(self):
        self.__bed()
        self.__info()
        self._meeting()

s = Student('yuziyue', '12345678901', 'zhang', 'yu')
s.get_status()

print(s._Student__mom)

公有属性和方法

公有属性和方法能够在类内被访问,也能够被实例访问。

class Student(object):

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

    def __str__(self):
        return self.name

    def get_name(self):
        return self.name

s = Student('yuziyue')
print(s.get_name())

动态绑定属性和方法

Python动态语言可以随时给一个对象绑定一个属性或方法。这样使得对象的使用非常简单。记住,这种绑定只对当前对象有效,对其他实例无效。

实例动态绑定属性

class Student(object):

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

    def __str__(self):
        return self.name

s = Student('yuziyue')
s.home = 'beijing'
print(s.home)

实例动态绑定方法

from types import MethodType

def set_age(self, age):
    self.age = age

class Student(object):

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

    def __str__(self):
        return self.name

s = Student('yuziyue')
s.set_age = MethodType(set_age, s)
s.set_age(23)
print(s.age)

类动态绑定属性,对该类的所有实例有效

school = 'beijing university'

class Student(object):

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

    def __str__(self):
        return self.name

s = Student('yuziyue')

Student.school = school
print(s.school)

类动态绑定方法,对该类的所有实例有效

def set_age(self, age):
    self.age = age

class Student(object):

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

    def __str__(self):
        return self.name

s = Student('yuziyue')
Student.set_age = set_age
s.set_age(23)
print(s.age)

限制动态绑定的属性和方法

  • 为了限制动态绑定的属性,可以使用 __slots__ 类的属性来限制,这样以来只有在  __slots__ 中指定的属性才可以通过动态绑定。
  • __slots__ 定义的属性仅对当前类实例起作用,对继承的子类是不起作用
  • 对于主要是用来当成简单的数据结构的类而言,使用 __slots__ 可以极大的减少实例所占的内存。__slots__ 更多的是用来作为一个内存优化工具,而非防止限制给实例添加新的属性。
class Student(object):

    __slots__ = ("name", "age")

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

    def __str__(self):
        return self.name

s = Student('yuziyue')
s.age = 23
print(s.age)

创建可管理的属性

实例的属性除了访问、修改外还可能要对其进行类型检查或合法性验证,使用 @property 装饰器可以解决该问题。 注意:类的属性必须是私有方法(_age 或者 __age,二选一,不能混合使用),调用时可当属性调用。在 __init__()  中,self.age = age 为什么会没有下划线? 因为想在初始化赋值时就对其进行合法性检验。 在普通实例方法中正常使用 self.age 这种格式调用 age 属性。

class Person(object):

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

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, age):
        if not (0 < age < 200):
            raise ValueError('age must between 0 and 200')
        if type(age) != int:
            raise ValueError('age must be a num')
        self.__age = age

    @age.deleter
    def age(self):
        raise AttributeError("Can't delete attribute")

    def status(self):
        print(self.age)

yzy = Person(23)
print(yzy.age)
yzy.age = 24
print(yzy.age)

yzy.status()

如果对属性的检查、修改函数都已经写好了,也可以使用 property 函数来实现上面的功能。

class Person(object):

    def __init__(self, age):
        self.set_age(age)

    def get_age(self):
        return self.__age

    def set_age(self, age):
        if not (0 < age < 200):
            raise ValueError('age must between 0 and 200')
        if type(age) != int:
            raise ValueError('age must be a num')
        self.__age = age

    def del_age(self):
        raise AttributeError("Can't delete attribute")

    age = property(get_age, set_age, del_age)

yzy = Person(23)
print(yzy.age)
yzy.age = 24
print(yzy.age)

抽象类的实现

如果想只在基类做一些方法名称的定义,具体的实现到子类实现,则可以使用抽象类,这种基类不能实例化,而子类继承的方法必须要有覆盖,否则报错,也就是说执行类型检查来确保子类实现了某些特定的方法。

import abc #利用abc模块实现抽象类

class All_file(metaclass=abc.ABCMeta):
    all_type='file'
    @abc.abstractmethod #定义抽象方法,无需实现功能
    def read(self):
        '子类必须定义读功能'
        pass
    @abc.abstractmethod #定义抽象方法,无需实现功能
    def write(self):
        '子类必须定义写功能'
        pass

#class Txt(All_file):
#    pass
#
#t1=Txt() #报错,子类没有定义抽象方法

class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('文本数据的读取方法')
    def write(self):
        print('文本数据的读取方法')


class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('硬盘数据的读取方法')
    def write(self):
        print('硬盘数据的读取方法')

wenben=Txt()
yingpan=Sata()

#这样大家都是被归一化了,也就是一切皆文件的思想
wenben.read()
yingpan.write()

print(wenben.all_type)
print(yingpan.all_type)

继承

  • 子类继承父类的属性、方法,如果子类需要更多属性、方法时,则需要子类自己新增
  • 需要在子类中使用 super().__init__() 的方式初始化父类
  • 如果子类的属性、方法不能满足子类时,则需要子类自己覆盖重写该属性、方法
  • 父类的属性、方法中,__ (双下划线)开头的不能继承,只能继承公有属性、_(单下划线)开头的
class Animal(object):
    def __init__(self,name):
        self.name = name
    def run(self):
        print('Animal is running...')


class Dog(Animal):
    def __init__(self,name):
        super().__init__(name)
    def run(self):
        print('Dog is running...')
    def eat(self):
        print('Dog is eating bone...')


class Cat(Animal):
    def __init__(self,name):
        super().__init__(name)
    def run(self):
        print('Cat is running...')
    def eat(self):
        print('Cat is eating rats...')


a = Animal('animal')
d = Dog('dog')
c = Cat('cat')

print(a.name)
print(d.name)
print(c.name)

a.run()
d.run()
c.run()

多重继承

  • 多重继承的常用方式: 在使用多重继承是要避免使用链式继承 (动物->肉食动物->飞行动物->鸟),因为这样继承关系往往会很复杂。在设计类的继承关系时,通常,主线都是单一继承下来的,如果要加"功能",通过多重继承即可,这种方式叫做 MixIn ,比如狗的主线是 Mammal, 其他 "功能" 就是 RunnableMixIn, KeeperMixIn, LoyaltyMixIn
class Dog(Mammal, RunnableMixIn, KeeperMixIn, LoyaltyMixIn):
    pass
    
  • 多重继承中的 __mro__ 列表: 在对象初始化时,需要寻找对象的属性所在的类,该实例根据它的类的一个 __mro__ 列表来判断属性怎么初始化,属性、方法的调用顺序。为了实现继承,Python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
    mro 列表构造的三条准则:
    1.子类会先于父类被检查
    2.多个父类会根据它们在列表中的顺序被检查
    3.如果对下一个类存在两个合法的选择,选择第一个父类


    注意: 子类调用super()时不一定是调用直接父类,而是直接从 mro 列表获取下一个父类,看下面的例子
class A():
    def example(self):
        print("A's example!")
        super().example()

class B():
    def example(self):
        print("B's example!")

class C(A, B):
    def example(self):
        print("C's example!")
        super().example()

c = C()
print(C.__mro__)
c.example()

# 结果
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
C's example!
A's example!
B's example!

A() 没有父类,但是它用了super().example(),从这里可以看出了 mro 的作用, 调用顺序是:C、A、B

  • 多重继承父类需要参数进行初始化:如果在多继承中子类初始化时,需要传递参数给多个父类初始化,这时就需要使用 默认参数关键字 参数配合完成。 实例初始化时,使用关键字参数的方式调用。  子类 __init__()中只负责初始化提供的默认参数列表,其他的参数通过 kwargs 传递到 mro 列表的下一个类进行初始化。 如果父类想知道子类的属性值,可以在子类初始化时显示传递到父类 super().__init__(sub=sub, **kwargs), 父类用显示关键字接收 __init__(self, sub='', **kwargs) ,下面是例子。
import inspect

class Baseclass(object):
    def __init__(self, base='', **kwargs):
        super().__init__(**kwargs)
        self.base = base
    def fbase(self):
        print('Baseclass class')


class A(Baseclass):
    def __init__(self, a='', **kwargs):
        super().__init__(**kwargs)
        self.a = a
    def fa(self):
        print('A class')


class B(Baseclass):
    def __init__(self, b='', **kwargs):
        super().__init__(**kwargs)
        self.b = b
    def fb(self):
        print('B class')


class C(Baseclass):
    def __init__(self, c='', sub='', **kwargs):
        super().__init__(**kwargs)
        self.c = c
        print("I am father C, and I know sub is {}!".format(sub))
    def fc(self):
        print('C class')


class Subclass(A, B, C):
    def __init__(self, sub='', **kwargs):
        super().__init__(sub=sub, **kwargs)
        self.sub = sub
    def fsub(self):
        print('Subclass class')

yuchaoshui = Subclass(base='1', a='2', b='3', c='4', sub='5')
print(Subclass.__mro__)
print(inspect.getmro(Subclass))

print(yuchaoshui.base)
print(yuchaoshui.a)
print(yuchaoshui.b)
print(yuchaoshui.c)
print(yuchaoshui.sub)


# 结果如下:
I am father C, and I know sub is 5!
(<class '__main__.Subclass'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.Baseclass'>, <class 'object'>)
(<class '__main__.Subclass'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.Baseclass'>, <class 'object'>)
1
2
3
4
5

多态

  • 向不同的对象发送同一条消息,不同的对象在接收消息时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
  • 实现方法
    1、继承父类然后重载方法,子类和父类调用相同的方法,会有不同的结果。
    2、实例作为参数传递给一个函数,在函数里面调用相同方法,会有不同的结果。
class AudioFile:
    def __init__(self, filename):
        if not filename.endswith(self.ext):
            raise Exception("Invalid file format")
        self.filename = filename

class MP3File(AudioFile):
    ext = ".mp3"
    def play(self):
        print("Playing {} as mp3".format(self.filename))

class WavFile(AudioFile):
    ext = ".wav"
    def play(self):
        print("Playing {} as wav".format(self.filename))

class OggFile(AudioFile):
    ext = ".ogg"
    def play(self):
        print("Playing {} as ogg".format(self.filename))

class FlacFile:
    def __init__(self, filename):
        if not filename.endswith(".flac"):
            raise Exception("Invalid file format")
        self.filename = filename
    def play(self):
        print("Playing {} as flac".format(self.filename))

mp3 = MP3File("file.mp3")
wav = WavFile("file.wav")
ogg = OggFile("file.ogg")
flac = FlacFile("file.flac")

mp3.play()
wav.play()
ogg.play()
flac.play()

特殊属性特殊方法

dir() 函数可以打印出一个对象的所有属性、方法。返回一个 list,比如 print(dir(instancename))

  • __dict__
    保存对象的所有属性,是一个字典,key是属性名, value是属性值。
  • __dir__()
    当 全局函数 dir() 使用时调用该方法。返回值是一个list,存储该对象的所有属性、方法。
  • __slots__
    限制动态给实例添加的属性。__slots__ 更多的是用来作为一个内存优化工具。__slots__ 属性来极大的减少实例所占的内存。当在创建大量的实例时,可以使用这种方法,但是一旦使用了 __slots__ 实例的很多特性都不能使用了。
  • __str__()
    返回实例的默认打印属性。
  • __repr__()
    同上,不同点是 __repr__ 是给调试人员使用的。
  • __iter__()
    如果一个类想被用于 for ... in 循环,类似listtuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象。
  • __getitem__()
    实现该方法,可以像list那样使用访问元素的第几位,比如 Fib(10)[5]
  • __setitem__()
    实现该方法,把对象视作list或dict来对集合赋值。Fib(10)[5] = 3
  • __delitem__()
    实现该方法, 用于删除某个元素。
  • __getattribute__
    获取实例属性时调用该函数(属性、方法),看下面示例。
class A:
    def __init__(self, name):
        self.name = name
    def sleep(self):
        print("{} is sleeping.".format(self.name))
    def eat(self):
        print("{} is eating.".format(self.name))

    @classmethod
    def life(cls):
        print("everyone has a life!")

    def __getattribute__(self, attribute_name):
        print("__getattribute__ is called!")
        return super().__getattribute__(attribute_name)

a = A('yuziyue')
print(a.name)
print()
a.sleep()
print()
a.eat()
print()
A.life()
  • __getattr__()
    当实例获取不存在的属性时会调用该方法, 返回该方法的返回值,可以返回一个值,也可以返回函数。
  • __call__()
    定义该方法后,可以在实例后面加括号当函数来调用,调用的就是 __call__()  方法。
  • __format__()
    该方法接收一个需要格式化的格式返回一个格式化好的字符串,最终当使用 format 格式化对象时,调用对象的该方法。比如 format(d, 'ymd')
  • __new__()
    是构造函数, 实例化对象时,先调用它实例化一个对象,然后才调用 __init__() 初始化对象的属性值。
  • __enter__()
    让对象支持 with 语句时, 要实现该方法。
  • __exit()__
    让对象支持 with 语句时, 要实现该方法。with语句块结束是调用该方法。
  • __del__()
    该方法是当一个对象的生命周期结束时被调用,做清理工作。
  • __get__() 、__set__() 、 __delete__()
    拥有这三个函数的类就是一个描述器,用于对类实例的属性进行合法性检查。

元类

元类是类的一种。正如类定义了实例的功能,元类也定义了类的功能。类是元类的实例。

使用 type 元类创建类

通过type()函数创建的类和直接写class是完全一样的, 要创建一个class对象,type()函数依次传入3个参数

a. class的名称;

b. 继承的父类集合,以tuple的形式给出;

c. class的方法名称与函数绑定。

(下面只是一个例子,平时并不会这么使用)

def __init__(self):
    self.name = 'noboby'

def get_name(self):
    return self.name

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

Person = type('Person', (object,), dict(get_name=get_name, set_name=set_name, __init__=__init__))

p = Person()
print(p)
print(p.name)
p.set_name('yzy')
print(p.name)
print(p.get_name())


# 结果如下:
<__main__.Person object at 0x7f18bf75a6d8>
noboby
yzy
yzy

自定义的Mylist增加add方法

cls、name、 bases、 attrs 分别表示:当前创建的类、 类的名字、类继承的父类集合、类的方法集合。

class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)

class Mylist(list, metaclass=ListMetaclass):
    pass

urls = Mylist()
urls.add("http://www.baidu.com/")
urls.add("http://www.kekenet.com/")
print(urls)

利用元类编写简单 ORM 框架

class Field(object):
    def __init__(self, name, column_type):
        self.name = name
        self.column_type = column_type
    def __str__(self):
        return '<%s:%s>' % (self.__class__.__name__, self.name)

class StringField(Field):
    def __init__(self, name):
        super(StringField, self).__init__(name, 'varchar(100)')


class IntegerField(Field):
    def __init__(self, name):
        super(IntegerField, self).__init__(name, 'bigint')

class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        if name=='Model':
            return type.__new__(cls, name, bases, attrs)
        print('Found model: %s' % name)
        mappings = dict()
        for k, v in attrs.items():
            if isinstance(v, Field):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v
        for k in mappings.keys():
            attrs.pop(k)
        attrs['__mappings__'] = mappings
        attrs['__table__'] = name
        return type.__new__(cls, name, bases, attrs)


class Model(dict, metaclass=ModelMetaclass):
    def __init__(self, **kw):
        super(Model, self).__init__(**kw)
    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Model' object has no attribute '%s'" % key)
    def __setattr__(self, key, value):
        self[key] = value
    def save(self):
        fields = []
        params = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v.name)
            params.append('?')
            args.append(getattr(self, k, None))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))

class User(Model):
    id = IntegerField('id')
    name = StringField('username')
    email = StringField('email')
    password = StringField('password')

u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
u.save()

使用元类优雅地实现单例模式

class Singleton(type):
    def __init__(self, *args, **kwargs):
        self.__instance = None
        super().__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            self.__instance = super().__call__(*args, **kwargs)
        return self.__instance

class Spam(metaclass=Singleton):
    def __init__(self):
        print('Creating Spam')

s = Spam()
s1 = Spam()
print(s is s1)

单例模式表示该类只能有一个实例,type 表示元类,Singleton继承至元类。 __call__() 表示它所在的类使用括号()时被调用,SpamSingleton 的一个实例,s、s1Spam的实例。

如果不使用元类,则必须要使用一个全局变量来保存实例了。

class Singleton:
    def __init__(self):
        print('Creating.')

settings_instance = None

def Settings():
    global settings_instance
    if settings_instance is None:
        settings_instance = Singleton()
    return settings_instance

s = Settings()
s1 = Settings()
print(s is s1)

或者使用类的静态方法,看下面示例:

class Foo(object):

    __instance = None

    @staticmethod
    def singleton():
        if Foo.__instance:
            return Foo.__instance
        else:
            Foo.__instance = Foo()
            return Foo.__instance

f = Foo.singleton()
f1 = Foo.singleton()
print(f is f1)

但后面两种方法并不是那么优雅,哈哈哈哈。

在类上强制使用编程规约

如果希望强制执行某些编程规约来帮助程序员保持清醒,比如继承的子类类属性方法名不允许大小写混写。

class NoMixedCaseMeta(type):
    def __new__(cls, clsname, bases, clsdict):
        for name in clsdict:
            if name.lower() != name:
                raise TypeError('Bad attribute name: ' + name)
        return super().__new__(cls, clsname, bases, clsdict)

class Root(metaclass=NoMixedCaseMeta):
    pass

class A(Root):
    def foo_bar(self):
        pass

class B(Root):
    def foobar(self):
        pass

class C(Root):
    School = 'beijing university'
    def __init__(self):
        pass

a=A()
b=B()
c=C()
🔗

文章推荐