python面向对象高级

1. 实例方法、类方法、静态方法

python类中定义的方法,一般需要通过实例才能访问,也就是实例方法。python类中定义的方法一般有三种:实例方法、类方法、静态方法。这三种方法在内存中均属于类的内存空间,但是调用方式不同。

1.1 实例方法
  • 实例方法必要参数self,指向实例对象本身
  • 实例方法只能由实例对象调用,不可使用类名调用
# 实例方法
class Test(object):
    
    def info(self):  
        print("我是一个实例方法!")
        
test = Test()
test.info()  # 实例对象调用实例方法
# Test.info()  # 类名调用实例方法报错

# 执行结果  
我是一个实例方法!
1.2 类方法
  • 使用内置装饰器@classmethod 放在函数前面,表示这是一个类方法
  • 类方法必要参数参数为cls
  • 类方法可被类和实例调用
# 类方法
class Test(object):
    
    @classmethod
    def info(cls):  
        print("我是一个类方法!")
        
Test.info()  # 类名调用类方法
test = Test()
test.info()  # 实例对象调用类方法

# 执行结果 
我是一个类方法! 
我是一个类方法!
1.3 静态方法

若要定义一个方法,该方法既不需要访问实例属性或者调用实例方法,也不需要访问类属性或者调用类方法,就可定义为静态方法。

  • 使用内置装饰器@staticmethod 放在函数前面,表示这是一个静态方法
  • 静态方法没有参数
  • 静态方法可被类和实例调用
# 静态方法
class Test(object):
    
    @staticmethod
    def info():  
        print("我是一个静态方法!")
        
Test.info()  # 类名调用静态方法
test = Test()
test.info()  # 实例对象调用静态方法

# 结果 
我是一个静态方法!
我是一个静态方法! 

2. 特殊方法

在 Python 中,我们可以经常看到以双下划线 __ 包裹起来的方法,比如最常见的 __init__,这些方法被称为魔法方法(magic method)或特殊方法(special method)。简单地说,这些方法可以给 Python 的类提供特殊功能,方便我们定制一个类,比如 __init__ 方法可以对实例属性进行初始化。

2.1 __init__方法
  • 类可以起到模板的作用,创建实例时会自动调用__init__方法,初始化对象的属性。
  • __init__方法可理解为对象的初始化方法,第一个参数为self,指向创建的实例本身。
  • __init__方法没有返回值,若有返回值会导致错误。
  • 定义了__init__方法后,创建实例时应传入对应的参数
# __init__方法
class Test(object):
    
    # __init__方法定义了两个实例属性,创建实例时需要传入该参数,方可对实例对象初始化
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
test = Test("fengdi", 20)
# 判断实例对象是否有__init__方法定义的属性
print(hasattr(test, "name"))
print(hasattr(test, "age"))

# 执行结果 
True 
True 
2.2 __new__方法

在 Python 中,使用类创建实例过程:类会先调用 __new__(cls[, ...]) 来创建实例,然后 调用__init__ 方法对该实例(self)进行初始化。

关于 __new__ 和 __init__ 有几点需要注意:

  • __new__ 是在 __init__ 之前被调用的,__new__ 是类方法,__init__ 是实例方法。
  • __new__方法调用后会为对象分配空间,并返回实例对象引用;__new_方法若不返回实例或返回其他任意类的实例,将不会调用__init_方法
  • 重载 __new__ 方法,需要返回类的实例,一般使用super()调用父类的__new__方法返回实例对象应用(参数为cls)。
# __new__方法
class Test(object):
    
    def __new__(cls):
        print("__new__ called.")
        return super().__new__()
    
    def __init__(self, name):
        print("__init__ called.")
        self.name = name
        
test = Test("fengdi")

# 执行结果:可看出创建实例时会先调用__new__方法,后使用__init__方法进行初始化
__new__ called.
__init__ called.
class Test(object):
    
    # __new__方法未返回实例对象引用
    def __new__(cls, name):
        print("__new__ called.")
    
    def __init__(self, name):
        print("__init__ called.")
        self.name = name
        
test = Test("fengdi")

# 执行结果:可看出__new__方法若不返回实例对象或者返回其他任意类的实例,将不会调用__init__方法
__new__ called.
2.3 __str__方法

__str__是实例方法,当使用print打印对象时自动调用对象的__str__方法(默认只打印对象的内存地址)

# __str__方法
class Test(object):
    
    pass

test = Test()
print(test)
        
class Test(object):
    
    def __init__(self, name):
        self.name = name
    
    def __str__(self):
        return "Animal object:%s" % self.name

test = Test("fengdi")
print(test)

# 执行结果 
<__main__.Test object at 0x10b616630> 
Animal object:fengdi 
2.4 __del__方法
  • 在 Python 中

    • 当使用 类名() 创建实例对象时,为对象分配完空间后,自动 调用 __init__ 方法
    • 当一个对象被从内存中销毁前,会自动调用 __del__ 方法(del方法删除对象或者程序运行结束都会导致对象从内存中销毁)
  • 应用场景

    • __init__ 改造初始化方法,可以让创建对象更加灵活
    • _del_ 如果希望在对象被销毁前,再做一些事情,可以考虑一下 __del__ 方法
  • 生命周期

    • 一个对象从调用类名() 创建,生命周期开始
    • 一个对象的 __del__ 方法一旦被调用,生命周期结束
    • 在对象的生命周期内,可以访问对象属性,或者让对象调用方法
# __del__方法
class Test(object):
    
    def __init__(self, name):
        self.name = name
        print("对象%s生命周期开始" % self.name)
        
    def __del__(self):
        print("对象%s生命周期结束,对象销毁!" % self.name)
        
test = Test("fengdi")
del test

# 执行结果 
对象fengdi生命周期开始 
对象fengdi生命周期结束,对象销毁!   
对象fengdi生命周期结束,对象销毁! 
2.5 __call__方法

__call__:一般使用obj.method()来调用对象方法,定义__call__方法可以让实例可进行调用。

# __call__方法
class Test(object):
    
    def __init__(self, num1, num2):
        self.num1 = num1
        self.num2 = num2
        
    def __call__(self, num3):
        return self.num1 + self.num2 + num3
    
test = Test(5, 6)
print(callable(test))  # 验证对象是否可被调用
result = test(7)
print(result)

# 结果 
True 
18 
2.6 __iter__ __next__

若希望实例对象可迭代,需要在类中定义__iter__方法。若希望生成一个迭代器,需要在类中定义__iter__方法、__next___方法。__iter__方法返回一个具有__next__方法的对象。__next__方法返回容器的下一个元素,没有后续元素抛出StopIteration异常。具体内容参考迭代器生成器文章即可。

3. __slots__

在python中,我们定义类的时候可定义属性和方法,创建一个类的实例后,可动态地给该实例绑定任意新的属性和方法。如下:

# __slots__变量
class Test(object):
    
    def __init__(self, name):
        self.name = name
        
test = Test("fengdi")
print(test.__dict__)  # __dict__变量存储对象的所有属性
test.city = "wuhan"  # 动态添加属性
print(test.__dict__)  # __dict__变量存储对象的所有属性

# 执行结果
{'name': 'fengdi'}
{'name': 'fengdi', 'city': 'wuhan'}

当我们在类中使用__slots__特殊变量时,就会限制所能添加的属性。而且__slots__仅对当前类有效,对继承的子类无效。除非子类也定义__slots__,此时子类所能允许添加的属性为父类的__slots__和自身的__slots__所能允许的并集。

class Test(object):
    
    __slots__ = ("name", "age")
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
test = Test("yuxi", 20)
# test.city = "wuhan"  会报错,__slots__会限制实例对象添加的属性

4. property属性

property属性指的是:可将实例方法转变为实例属性的使用方式。默认情况下,实例方法使用类似于函数需要加括号调用,当设置property属性后可以像实例属性一样使用不用加括号。

实现property属性的有两种方式

  • 装饰器 即:在方法上应用装饰器
  • 类属性 即:在类中定义值为property对象的类属性
4.1 装饰器方式
  • 新式类提供三种装饰器:@property、@方法名.setter、@方法名.deleter
  • 可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
# property:装饰器实现
class Goods(object):

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price
 
    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price

goods = Goods()
print(goods.price)  # 获取商品价格
goods.price = 90   # 修改商品原价,调用@price.setter
print(goods.price)
del goods.price  # 删除商品原价,调用@price.deleter

# 执行结果

80.0
72.0
4.1 类属性方式
  • 当使用类属性的方式创建property属性时,经典类和新式类无区别

  • property方法中有个四个参数:

    • 第一个参数是方法名,调用 对象.属性 时自动触发执行方法
    • 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
    • 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
    • 第四个参数是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息
# property:类属性实现
class Goods(object):

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

    def get_price(self):
        return self.price

    def set_price(self, value):
        self.price = value

    def delete_price(self):
        del self.price
    
    BAR = property(get_price, set_price, delete_price)
    
goods = Goods(100)
print(goods.BAR)  # 自动调用get_price方法
goods.BAR = 90  # 自动调用set_price方法
print(goods.BAR)  
del goods.BAR  # 自动调用delete_price方法

# 执行结果 
100 
90

5. 单例模式

设计模式是前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对某一特定问题的成熟的解决方案。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。单例模式通常用于日志记录、数据库操作等,保证程序运行过程中只产生一个实例,以避免对同一资源产生相互冲突的请求。

  • 单例设计模式

    • 目的 —— 让类创建的对象,在系统中只有唯一的一个实例(对类创建对象的_new_方法进行重写)
    • 每一次执行 类名() 返回的对象,内存地址是相同的
  • 重写__new__方法

    • 重写 __new__ 方法 一定要 return super().__new__(cls)

    • 否则 Python 的解释器 得不到分配了空间的对象引用,就不会调用对象的初始化方法

    • 注意:__new__ 是一个静态方法,在调用时需要主动传递 cls 参数

# 单例模式
class Test(object):
    
    instance = None # 定义一个类属性保存实例对象引用
    
    def __new__(cls):
        # 若instance属性为空,则说明是第一次生成实例对象,调用父类__new__方法生成对象并保存。
        if cls.instance is None:
            cls.instance = super().__new__(cls)
        return cls.instance
    
test1 = Test()
test2 = Test()
print(id(test1), id(test2))

# 执行结果 
4489636440 4489636440
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270