私有属性和私有方法

在属性名或方法名前面加上 __ 的就是私有

class Father:
    def __init__(self):
        # 私有属性
        self.__money = "1000万"

    # 私有方法
    def __drinking(self):
        print("我喜欢喝酒")

    def get_money(self):
        print("我是你爹,我有钱")
        print("我有%s,但我就是不给你" % self.__money)
        self.__drinking()


class Child(Father):
    def get_money(self):
        print("我没钱")
        # 私有属性和方法,子类也无法访问
        # print("我爹有%s" % self.__money)
        # self.__drinking()

father = Father()
# 私有属性和方法,无法通过对象访问
# father.__money
# father.__drinking()
father.get_money()

child = Child()
child.get_money()
  • 类的私有属性 和 私有方法,都不能通过对象直接访问,但是可以在本类内部访问
  • 类的私有属性 和 私有方法,都不会被子类继承,子类也无法访问
  • 私有属性 和 私有方法 往往用来处理类的内部事情,不通过对象处理,起到安全作用

修改私有属性的值

私有属性不能直接访问,所以不能直接修改。但可以定义一个公有方法,在这个公有方法内访问修改。

class Father:
    def __init__(self):
        self.__money = "1000万"

    def set_money(self, money):
        self.__money = money

    def get_money(self):
        print("我是你爹,我有钱")
        print("我有%s,但我就是不给你" % self.__money)


father = Father()
father.set_money("一个亿")
father.get_money()

类属性、类方法与静态方法

  • 类属性:类属性就是类自身所拥有的属性,它被类所有的实例对象所共有。它在内存中只存在一个副本,这和 C++ 中类的静态成员属性有点类似。对于公有的类属性,在类外可以通过类对象实例对象访问。
  • 类方法:类方法就是属于类自身的方法。类方法需要用修饰器 @classmethod 来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以 cls 作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以 cls 作为第一个参数的名字,就最好用 cls 了),能够通过实例对象和类对象去访问。
  • 静态方法:需要通过修饰器 @staticmethod 来进行修饰,静态方法不需要多定义参数,可以通过对象和类来访问。
class Animal:
    # 类属性
    name = "小猪皮皮"

    # 类方法
    @classmethod
    def run(cls):
        # cls 就是类本身
        print("我叫%s" % cls.name)
        print("我会跑")

        # 静态方法
    @staticmethod
    def eat():
        # 没有参数,如果要用类本身,就直接写 类名
        print("我叫%s" % Animal.name)
        print("我要吃")


print(Animal.name)
Animal.run()
Animal.eat()

# 也可以实例化后再调用
# animal = Animal()
# animal.run()
# animal.eat()
  • 从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象 cls,那么通过 cls 引用的必定是类对象的属性和方法;
  • 实例方法的第一个参数是实例对象 self,但是通过 self 引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。
  • 静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类名来引用
  • 推荐使用类属性、类方法、静态方式时统一使用:类名.xxx 来访问,尽量不要使用对象来访问。

__new__ 方法

# Object为所有新式类的基类
class A(object):
    # cls 类自身
    def __new__(cls):
        print("这是 new 方法")
        # 得到 self 后,将 self,以及其他参数(如果有) 传给 __init__
        return object.__new__(cls)

    def __init__(self):
        print("这是 init 方法")


A()
  • __new____init__ 之前运行,作用就是实例化当前类(cls)。
  • __new__ 必须有返回值,返回一个实例化后的当前对象,就是 self
  • 可以 return super().__new__(cls) 出来的实例,或者是直接是 object.__new__(cls) 出来的实例
  • 接着会将 self 传给 __init__,此时 __init__ 运行。

单例模式

一个类不管实例化多少次,都只有一个实例对象。这样的类称为单例类。

class Singleton(object):
    __instance = None

    def __new__(cls, age, name):
        if not cls.__instance:
            cls.__instance = object.__new__(cls)
        return cls.__instance

# 实例化了两次
a = Singleton(10, "小猪皮皮")
b = Singleton(20, "小猪皮皮")

# 但都是同一个对象
print(id(a))
print(id(b))

# 设置a对象,b对象也有值
a.age = 4
print(b.age)
  • 先定义一个类属性 __instance,初始值设置为 None,表示当前类还没有被实例化。
  • 当第一次实例化, __new__ 第一次运行后,将实例化后的对象赋值给 cls.__instance,现在它就不是 None
  • 当再次实例化的时候,__new__ 第二次运行,因为 cls.__instance 已经有数据了,所以判断失败,那就直接返回第一次实例化的对象,不会再次实例化了。

只执行一次 __init__ 方法

class Singleton(object):
    __instance = None
    __is_first = True

    def __new__(cls, age, name):
        if not cls.__instance:
            cls.__instance = object.__new__(cls)
        return cls.__instance

    def __init__(self, age, name):
        if Singleton.__is_first:
            self.age = age
            self.name = name
            Singleton.__is_first = False


a = Singleton(4, "小猪皮皮")
b = Singleton(1, "鹦鹉波利")

print(id(a))
print(id(b))

print(b.name)
print(b.age)
  • 添加了一个类属性 __is_first,用来判断是否第一次实例化。
  • 接着在 __init__ 中判断,只有第一次实例化,才运行相关代码。
  • 第一次运行后,将 __is_first 设置为 False,再次运行,if 中的代码就不会再执行了

装饰器

def funA(fn):
    print("A")
    fn()
    return "hello"


@funA
def funB():
    print("B")


print(funB)

实际上执行了两步:

  • @funA 相当于执行 funA(funB),把 funB 作为参数,传给了 funA
  • funB 替换成 funA 的返回值。这样 funB 就不再是函数了,而是一个字符串 hello