python 类的成员及继承

类变量和实例变量

变量定义在类中,和类中的方法同一级别,就是类变量。

定义在实例中,绑定在 self 上的变量,是实例变量。

class Car():
    region = 'China'                 # 类变量
    def __init__(self,brand,price):
        self.brand = brand           # 实例变量
        self.price = price


car = Car('奔驰',300000)
car.owner = 'LaoWang'  # 实例化对象动态添加变量
car.region = 'Beijing' # 和类变量 Car.region 重名了也没关系
print(car.region)      # 当类变量和实例化变量同时存在,首先访问实例变量
print(Car.region)

单下划线和双下划线

在 Python 中,一个变量或方法如果以双下划线 __ 开头如 __xx,就是一个私有方法或私有变量,无法从外部访问

以单下划线 _ 开头的变量或方法,意味着是受保护的方法或变量,这只是约定俗成而已,这样写表示不希望这个变量在外部被直接调用,这种变量其实和普通变量是一样的。

以双下划线开头并以双下划线结尾的,如 __init__,是Python内置的方法名。

为了防止和现有的关键字重名,习惯对变量用单下划线结尾,如 type_ 来命名某些变量。

注意:以单下划线或双下划线开头的变量或函数,不能通过 from xx import * 自动导入(其他导入方式是可以的,譬如 from xx import _yy 是没问题的)。

class Base():
	var = "类变量"
	_protect_var = "受保护的变量"
	__private_var = "私有类变量"

	def __init__(self):
		self.base = 'base'

	def test(self):
		print("From Base.")

	def _protect(self):
		print("可以被子类继承,但无法被")

	def __private(self):
		print("Base private method.")


class A(Base):
	pass


a = A()
print(
	a.var,
	a._protect_var,
	a.test(),
	a._protect(),
)
print(dir(a))
print(a.__private_var, a.__private())  # 会报错

注意:dir(a) 打印出的 _Base__private, _Base__private_var 其实就是私有方法和私有变量 __private, __private_var,之所以无法访问私有变量,是因为被改名成 "_类名__函数或变量名" 了。

所以私有变量是可以通过这种方式访问的,不过不建议使用这种方式访问私有变量,因为既然设置成了私有,就意味着开发人员不想让你用它。

@staticmethod 静态方法

静态方法不能访问实例变量和类变量,除了身处类里面,它其实和类没有什么关系。放在类里面的好处是,我们可以通过类和实例调用这个方法。

class People(object):
    def __init__(self,name):
        self.name = name

    @staticmethod       # 静态方法,不能访问实例变量和类变量,和类实际没啥关系,只是将这个函数放在了类里面而已
    def getname(sex):
        print('XX is a %s' %(sex))

people = People('LaoWang')
people.getname('man')  # 通过实例访问
PeoPle.getname("girl")  # 通过类访问

classmethod 类方法

类方法只能访问类变量,不能访问实例变量。

class People(object):
    age = 30

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

    @classmethod   # 类方法,只能访问类变量,不能访问实例变量
    def getage(cls):
        print('age is %s' % cls.age)

people = People('LaoWang')
people.getage()

@property 属性方法

属性方法,把一个方法变成属性,调用时不加括号即可完成调用,就像调用一个属性一样。

class People(object):
    age = 30

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

    @property     # 属性方法,把一个方法变成属性。调用时不用加括号。
    def eat(self):
        print('%s is eating.' %self.name)

people = People('LaoWang')
people.eat   # 调用属性方法。不加括号

属性方法的修改和删除:

class People(object):
    age = 30

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

    @property     # 属性方法,把一个方法变成属性。调用时不用加括号。
    def eat(self):
        print('%s is eating.' %self.name)
        
    @eat.setter   # 属性的修改
    def eat(self,food):
        print('This is eat setter: %s' %food)
        
    @eat.deleter  # 属性的删除
    def eat(self):
        print('eat has been deleted.')

people = People('LaoWang')

people.eat              # 调用属性方法。不加括号
people.eat = 'suger'    # 调用eat.setter
del people.eat          # 调用eat.deleter

其他类成员

class T(object):
    '''this is doc'''
    def __new__(cls, *args, **kwargs):
        print('this is new func')
        return super().__new__(cls)

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

    def getname(self):
        print(self.name)

    def __getitem__(self, item):
        print('getitem:',item)

    def __setitem__(self, key, value):
        print('setitem:',key,value)

    def __delitem__(self, key):
        print('delitem:',key)

    def __call__(self, *args, **kwargs):
        print('this is call, you should add bracket')

    def __str__(self):
        return 'you are print obj'

    def __del__(self):
        print('When obj is been destroyed,this func would execution automatically.')


t=T('Wang')        # T 是一个类,它是由type创建的(type是所有类的类,它可以创建类对象,然后类对象可以创建实例)。而type里面定义了一个__call__函数,T 算是 type 的实例,所以才能使用 'T()'
print(T.__doc__)   # 打印类的文档注释
print(t.__module__)# 打印对象所处的模块名
print(t.__class__) # 打印对象的类
print(t.__dict__)  # 打印对象的变量
print(T.__dict__)  # 打印类的变量
print(t)           # 打印对象,打印 __str__ 方法的返回值(没有str方法,则打印对象的内存地址)
t()           # 对象直接加括号,调用 __call__ 方法
val = t['1']  # __getitem__
t['1'] = 1    # __setitem__
del t['1']    # __delitem__
del t         # __del__

__new__()方法

我们常说 __init__(self) 是构造方法,其实是不准确的,__init__(self) 中的 self ,其实就是一个实例,这个实例对象,是先由 __new__(cls)创建的, cls 是当前正在实例化的类。

也就是说,在 __init__() 执行之前,__new__() 已经对当前类执行并且返回了一个实例对象给 __init__(), 如果 __new__() 没有返回值,则 __init__() 根本不会执行。

如果一个类没有写 __new__() 方法,则会调用它父类的 __new__ 来生成实例并返回给当前类的 __init__ 方法。如果一个类写了 __new__ 方法,那么你可以自由选择任意一个的其他的新式类(必定要是新式类,因为所有新式类都是object的后代)的 __new__() 方法来制造实例。

可以看这篇博客以及博客的留言:[Python] Python 之 new() 方法与实例化 - iFantasticMe - 博客园 (cnblogs.com)

class A(object):
    def __new__(cls, *args, **kwargs):
        print('cls is ',cls)
        # 调用父类的__new__(),返回一个实例对象; 因为父类 object 的此方法只接受一个参数,所以只传递一个 cls
        self = super(A, cls).__new__(cls)
        self.x = 'XXX'  # 给实例对象设置一个 x 属性
        return self     # __new__必须返回一个实例,否则对象会是 NoneType;如果没有返回值,则__init__不会被调用执行

    def __init__(self):
        self.y = 'YYY'

        
# B继承A,会默认调用父类A的 __new__() 生成一个实例,这个实例自带一个 x 属性
class B(A):
    def __init__(self,name):
        self.z = name

        
a = A()
b = B('ZZZ')
print(b.__dict__)  # {'x': 'XXX', 'z': 'ZZZ'}

类的类

python中万物皆对象。类其实也是一个对象。在python中类可以创建实例,那么类本身又是谁创建的呢,答案是 type

num = type(1)
string = type('my')
dictionary = type({1:'1'})
print(num,string,dictionary)
# <class 'int'> <class 'str'> <class 'dict'>

print(num.__class__,string.__class__,dictionary.__class__)
# <class 'type'> <class 'type'> <class 'type'>

从上面的例子可以看出,数字、字符串、字典,它们的类型都是由不同的类创建的。而这些类再往上追溯,它们又都是由 type 类创建的。所以说,type就是所有类的类。

我们常见的 object,也是 type 的一个实例:

object 和 type 的关系:object 是 type 的实例,object 又是 type 的基类(type继承自object)

print(isinstance(object,type))  # True

通过type创建类:

# class A(object):
#     a = 5
#     def abc(self,name):
#         self.name = name
#     def getname(self):
#         print(self.name)


#  使用type创建一个类,和上面注释掉的类一模一样
def init(self, name):
    self.name = name
def getname(self):
    print(self.name)
A = type('A',(object,),{'a':5,'__init__':init,'getname':getname})
# type第一个参数:类名
# type第二个参数:当前类的基类
# type第三个参数:类的成员

aa = A('Wang')
aa.getname()
print(aa.a)

元类 metaclass

元类就是用来造类的类,type 也是一个元类,只不过它是 python 内建的元类,是所有类的类。我们也可以自己创建一个元类,用来动态的创建类。

class MyType(type):  # 创建元类,继承 type
    # 第一步,元类的操作都在__new__中完成。name 是将要创建的类的名字。bases 是将要创建的类的父类。attrs 是类的方法和属性的字典
    def __new__(cls, name, bases, attrs):
        print("元类 __new__", name, bases, attrs, sep=" | ")
        return type.__new__(cls, name, bases, attrs)  # 如果返回一个已经存在的实例或者不返回任何值,则 __init__ 不会被调用

    # 第二步,init
    def __init__(self, *args, **kwargs):
        print("元类 __init__", *args, **kwargs, sep=" | ")

    # 第三步,子类实例化时才执行。类实例化时需要加括号对吧,譬如: a = Foo(name), 这个括号就调用了 __call__ 方法。
    def __call__(self, *args, **kwargs):
        # 元类的实例是继承了元类的类
        print("元类 __call__,被子类继承,来实例化子类", *args, **kwargs)
        print("元类的实例,其实是继承了元类的类:", self)
        obj = self.__new__(self)
        print("元类的实例的实例:", obj)
        self.__init__(obj, *args, **kwargs)
        return obj


# 类 Foo 本身就是元类的实例,因此一旦声明了 Foo 类,其实元类的 __new__、__init__ 就已经执行了
class Foo(object, metaclass=MyType):
    def __new__(cls, *args, **kwargs):
        print("子类 __new__", cls, *args, **kwargs)
        return object.__new__(cls)

    def __init__(self, name):
        self.name = name
        print("子类 __init__")

    def getname(self):
        print('name is ', self.name)


# print(isinstance(Foo, MyType))  # Foo类,是 MyType 的一个实例

f = Foo("Wang")  # Foo() 会调用 __call__ 方法,自身没有这个方法,会去父类中查找

例子:使用元类来创建类,并动态的给所创建的所有类添加一个属性:类名 = 'Hello, '+类名

class Meta(type):      # 元类
    def __new__(cls, name,bases,attrs):  # name:类名, bases:基类, attrs:类的所有方法属性的字典。
        attrs[name] = 'Hello, %s' % name
        print(attrs)
        return type.__new__(cls,name,bases,attrs)

    
class People(object,metaclass=Meta): # 使用元类来定制类,People类是object和Meta的实例
    age = 22

    
peo = People()
print(peo.People)

# `People` 这个类本身就是 Meta 元类的实例,它是调用 `Meta.__new__()`, `Meta.__init__()` 方法后返回的一个实例
# `People()` 会尝试调用 Meta 元类的 `Meta.__call__` 方法,元类没有的方法,则向上查找父类的父类...
# 然后完成赋值:`peo = People()` 

使用元类作为单例模式:__call__()

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

    # __call__可以让类的实例像函数一样,直接使用()调用,譬如有个实例:a,a() 会直接调用 __call__() 函数
    def __call__(self, *args, **kwargs):
        print('__call__ is called.')
        if self.__instance is None:
            self.__instance = super().__call__(*args, **kwargs)
            return self.__instance
        else:
            return self.__instance

        
class Spam(metaclass=Singleton):  # Spam 是 Singleton 的实例
    def __init__(self):
        print('Creating Spam')

        
# Spam 是 Singleton的一个实例
a = Spam()  # Spam实例化时调用了 Singleton 的 __call__(),所以产生一个实例
b = Spam()  # 第二次调用,__call__() 直接返回同一个实例

print(a is b)

继承

Python 支持多继承,即可以继承多个父类。当子类继承了父类,那么子类就同时继承了父类所有的方法以及父类的类属性(父类的私有方法和私有类属性其实也会被继承,只不过会被改成 _类名__方法名 这种格式)。

譬如:

class Base():
    var = "类变量"
    _protect_var = "protect"
    __private = "private"
    
    def __init__(self):
        self.base = 'base'
        
    def test(self):
        print("From Base.")
        
    def _protect_method(self):
        print("From Base protect method")
    
    def __private_method(self):
        print("From Base private method")

class A(Base):
    pass

a = A()
print(dir(a))

但是如果我们重写了父类中的某个方法,就会覆盖父类的同名方法,为了能够继续使用父类的方法,可以这样做:

class Base():
    def __init__(self):
        self.base = 'base'

    def test(self, name):
        print(f"From Base: {name}")
        return name


class B(Base):
    def test(self, name):            # 重写 test 方法,会覆盖父类方法
        # 其他代码 ...
        ret = Base.test(self, name)  # 调用父类 Base.test()
        # 其他代码 ...

b = B()
b.test("xy")

其实也可以将上面类 B 的 test 函数改成别的名字如 tt:

class Base():
    def __init__(self):
        self.base = 'base'

    def test(self, name):
        print(f"From Base: {name}")
        return name


class B(Base):
    def tt(self, name):              # 1. 函数名改成 tt 方法
        # 其他代码 ...
        ret = Base.test(self, name)  # 2. 调用父类 Base.test(),其实是将类 Base 的 test 方法绑定在当前 self 对象上
        # 其他代码 ...

b = B()
b.test("xy")  # 3. 可以调用 test 方法,因为它已经绑定到当前实例对象 b 上了
b.tt("tt")

不继承某个类,却想要使用这个类的某个方法,也可以使用 类名.方法名(self, 参数...) 来调用类的方法:

class Base():
    def __init__(self):
        self.base = 'base'

    def test(self, name):
        print(f"From Base: {name}")


class B:  # 没有继承类 Base 哦
    def tt(self, name):
        Base.test(self, name)  # 只是调用了类 Base 的方法,并没有将 test 方法绑定在当前对象上

b = B()
print(dir(b))

多继承

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

    def test_a(self, msg):
        print(f"Test a: {msg}")


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

    def test_b(self, msg):
        print(f"Test b: {msg}")


class C(A, B):  # 继承了两个类,即多继承
    def __init__(self):
        x = "wzt"
        super().__init__(x)  # 1. 第一种

    def test_a(self, msg):
        super(C, self).test_a(msg)  # 2. 第二种

    def test_b(self, msg):
        B.test_b(self, msg)  # 3. 第三种


c = C()
c.test_a("c.test_a")
c.test_b("c.test_b")
print(C.mro())

上面介绍了多继承,在类 C 中,重写了父类的三个方法,又在重写的方法里面,使用了三种不同的方式来调用父类的方法。

  • 第一种:super().方法名(参数)
    • super 的原理是:可以按照 mro 顺序,找到 MRO 顺序列表中当前类 C 后面的类,然后将那个类的某个方法,绑定给 self 实例
  • 第二种:super(C, self).方法名(参数)
    • 和第一种一样,只不过写法不同而已
  • 第三种:父类.方法名(self, 参数)
    • 上面说过,super 只会查找 MRO 顺序表中当前类后面的那个类的某个方法(在本例中就是类 A);而当前这种方式可以指定继承哪个类的方法。

抽象类和抽象方法

from abc import ABCMeta, abstractmethod  # 导入抽象类和抽象方法


class Employee(metaclass=ABCMeta):   # 定义抽象类,抽象类不能被实例化,只能被继承!
    def __init__(self,name):
        self.name = name

    @abstractmethod             # 抽象方法,子类必须实现此方法!
    def get_salary(self):
        pass


class Manager(Employee):   # 子类
    def __init__(self,name):
        super().__init__(name)

    def get_salary(self):     # 实现抽象类的抽象方法
        return 15000


class Developer(Employee):
    def __init__(self,name,hour=0):
        self.name = name
        self.hour = hour
    def get_salary(self):
        return 200*self.hour


class Factory():  # 工厂模式
    @staticmethod
    def create(type,*args,**kwargs):
        return type(*args,**kwargs)

    
M = Factory.create(Manager,'ao')
D = Factory.create(Developer,'zhangsan',160)
print(f'{M.name} salary: {M.get_salary()}')
print(f'{D.name} salary: {D.get_salary()}')
posted @ 2019-11-13 13:16  wztshine  阅读(1447)  评论(0编辑  收藏  举报