Python 类(属性、方法、继承等)

本文深入探讨Python中的类,包括实例属性、类属性、私有属性及其访问,以及动态属性的概念。同时,介绍了方法的类型,如实例方法、静态方法,以及继承和多态的实现。文章还涵盖了类的初始化方法`__new__`与`__init__`的区别,`isinstance`与`type`的区别,多继承以及魔法方法的应用。此外,还讨论了继承中可能出现的属性值陷阱问题,展示了如何处理子类与父类同名属性的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Python里的类和和日常生活中说的类,如人类,鸟类,基本含义一致。即把具有相同属性和行为的一类事物定义为一个类。类是封装对象属性和行为的载体。这体现了类的封装性。同样作为鸟类,有的鸟两米高有的鸟却只有蜜蜂一般大,这体现的是类的多态性。大鸭子生的小鸭子会游泳,会呱呱叫,这可以理解是类的继承性。

1.属性

(1) 实例属性

如人这一类事物,应该有高矮、胖瘦、贫富等属性。具体到某一个人,他的高矮、胖瘦等属性就属于他自己的实例属性。

class Person():
    def __init__(self, height, weight, wealth):
        self.height = height
        self.weight = weight
        self.wealth = wealth


if __name__ == "__main__":
    p = Person(1.75, 60, 100)

(2) 类属性

但是,有些属性是类共有的,比如人类都有一颗爱心。不需要每个实例单独去实现。这样的属性叫类属性。

class Person():
    love = True
    def __init__(self, height, weight, wealth):
        self.height = height
        self.weight = weight
        self.wealth = wealth

(3) 私有属性

还有一些属性可能是隐私,不想被别人知道。这样的属性称为私有属性。比如长得矮的不想让别人知道身高,就需要设置访问权限。单下划线_height只允许类和子类访问,双下划线只允许本类访问,类的实例也不行。

class Person():
    def __init__(self, height, weight, wealth):
        self._height = height
        self.__weight = weight
        self.wealth = wealth

(4)属性访问

class Person:
    love = True

    def __init__(self, height, weight, wealth):
        self._height = height
        self.__weight = weight
        self.wealth = wealth


if __name__ == "__main__":
    p = Person(1.55, 70, 200)
    # 普通的实例属性和类属性都可以通过实例.属性方式访问
    print('我家有{}万财产'.format(p.wealth), '我{}爱心'.format('有' if p.love else '没有'))
    # 单下划线并没有真正实现属性保护,只是防止了直接通过p.height获取
    print(p._height)
    # 双下划线表示隐私性很强了,只有下面的方式可以访问,但仍然可以访问
    print(p._Person__weight)

# 输出:我家有200万财产 我有爱心
       1.55
       70

(5)添加和修改属性

class Person:
    love = True

    def __init__(self, height, weight, wealth):
        self._height = height
        self.__weight = weight
        self.wealth = wealth


if __name__ == "__main__":
    p = Person(1.55, 70, 200)
    p2 = Person(1.7, 60, 10)
    # 修改实例属性值p.wealth
    p.wealth = 1000
    print('我现在的财富变成了{}万'.format(p.wealth))
    # 这个操作相当于是新增属性,并不会修改类的对应属性
    p.love = False
    print('一个败类并不会影响全人类的特性,我是{}爱心的'.format('有' if p2.love else '没有'))
    # 新增实例属性
    p2.__dict__['system'] = '资本主义'
    print('曾经我认为{}制度是最优的'.format(p2.system))
    # 修改实例属性
    p2.__dict__['system'] = '共产主义'
    print('现在我坚定地认为{}制度才是是最优的'.format(p2.system))

# 输出:我现在的财富变成了1000万
       一个败类并不会影响全人类的特性,我是有爱心的
       曾经我认为资本主义制度是最优的
       现在我坚定地认为共产主义制度才是是最优的

(6)动态属性

前面讲了,你的身高可能是私有属性。但你对外需要报一个身高,那么我们可以使用动态属性。即把方法的调用变成和属性调用一样。

class Person:
    love = True

    def __init__(self, height, weight, wealth):
        self._height = height
        self.__weight = weight
        self.wealth = wealth

    @property
    def height(self):
        return self._height + 0.1


if __name__ == "__main__":
    p = Person(1.55, 70, 200)
    print('我对外公布的身高是{}'.format(p.height))

# 输出:我对外公布的身高是1.65

2.方法

(1)实例方法

方法就是前面提到的行为。比如,不管贫穷还是富贵,我们都要吃饭吧。实例方法的特点就是参数里有self,即实例本身。因此,你可以在方法里使用这个实例。

class Person:
    love = True

    def __init__(self, height, weight, wealth):
        self._height = height
        self.__weight = weight
        self.wealth = wealth

    @property
    def height(self):
        return self._height + 0.1

    def eat(self):
        if self.wealth > 100:
            self.wealth -= 1
            print('我花1万吃了一顿饭')
        else:
            self.wealth -= 0.01
            print('我花1百吃了一顿饭')


if __name__ == "__main__":
    p = Person(1.55, 70, 200)
    p.eat()
    print('我现在的余额是{}'.format(p.wealth))

# 输出:我花1万吃了一顿饭
       我现在的余额是199

(2)静态方法

顾名思义,就是方法是静态的,固定的,与实例无关的。实际上这个特点和在类外定义的函数一致。之所以还要在类里定义静态方法,主要还是从业务需求出发。比如,有的方法在其他地方用不到,只在某个类里使用。为了使代码有较强封装性,就在这个类里写静态方法。

class Person:
    love = True

    def __init__(self, height, weight, wealth):
        self._height = height
        self.__weight = weight
        self.wealth = wealth

    @staticmethod
    def think():
        print('仰望星空,思考人生')


if __name__ == "__main__":
    p = Person(1.55, 70, 200)
    p.think()

3.继承

(1)继承和方法重写

我们和父辈长得像,这是继承他们的基因。同样,中华民族的优秀品质和文化也被一代一代继承至今。在程序开发中,类的继承的意义就是在复制的基础上新增和修改。尽可能地避免重复代码。

class Person:
    love = True

    def __init__(self, height, weight, wealth):
        self._height = height
        self.__weight = weight
        self.wealth = wealth

    @staticmethod
    def think():
        print('仰望星空,思考人生')


class Student(Person):
    def __init__(self, height, weight, wealth, school):
        super().__init__(height, weight, wealth)
        self.school = school

    # 这里是方法重写,学生和普通人思考地问题不一样
    # 如果没有定义,学生类仍然可调用think方法,因为从人类那里继承来的
    @staticmethod
    def think():
        print('今天学了什么内容,晚上找莉莉散个步')

    # 新增了属于学生类的方法
    @staticmethod
    def do_homework():
        print('做一张试卷')
        
        
if __name__ == "__main__":
    s = Student(1.55, 70, 200, '河海大学')
    s.think()
    s.do_homework()

(2)调用父类一般方法

上面的例子中,我们已经调用了父类的构造方法。实际上,父类的其他方法都可以在子类中调用。

class Father:
    love = True

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

    @staticmethod
    def give():
        return 100


class Son(Father):
    def __init__(self, wealth):
        self.wealth = wealth
        super().__init__()

    def buy_house(self):
        # 调用父类的方法
        get = super().give()
        print('爸爸给了我{}万'.format(get))
        self.wealth += get
        house_price = 120
        self.wealth -= house_price


if __name__ == "__main__":
    s = Son(20)
    s.buy_house()
    print('买完房后,我的余额{}'.format(s.wealth))

# 输出:爸爸给了我100万
       买完房后,我的余额0

(3)强制要求子类实现某方法

为了保证功能,父类有时候需要要求子类在继承时必须实现某些方法。方法一是调用父类方法时抛异常。

class Father:
    def earn_money(self):
        raise NotImplemented


class Son(Father):
    def say(self):
        print('baba')


s = Son()
s.say()
# 子类没有实现这个方法,调用会报错
s.earn_money() 

另一个方法是定义抽象基类和抽象方法,这样子类如果没有覆盖该抽象方法,则子类实例化时就会报错。

class Father(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def earn_money(self):
        pass


class Son(Father):
    def say(self):
        print('baba')


# 实例化就会报错
s = Son()

4.其他知识

(1)new和init的区别

class User:
    # 对类进行操作, 执行顺序先于__init__
    def __new__(cls, *args, **kwargs):
        print("new")
        if kwargs['age'] < 18:
            return
        # 此处如不返回类,则不会调用__init__
        return super().__new__(cls)

    # 对实例进行操作
    def __init__(self, name):
        print("init")
        self.name = name


if __name__ == "__main__":
    user = User(name="bob", age=17)

(2)isinstance和type的区别

class A:
    pass


class B(A):
    pass


b = B()
# 只要有继承关系,不一定是直接继承
print(isinstance(b, B))
print(isinstance(b, A))

print(type(b) is B)
# type只会往上查询一级
print(type(b) is A)

# 输出:True
       True
       True
       False

(3)多继承

class A:
    pass


class B:
    pass


class C(B, A):
    pass

(4) 魔法方法

在类里定义一些魔法方法,就可以使类具备某些特性。

class Company:
    def __init__(self, employ_list):
        self.employ = employ_list

    # 实现这个方法就具备求长度的性质    
    def __len__(self):
        return len(self.employ)
    
    # 实现这个方法就具备可迭代性质
    def __getitem__(self, item):
        return self.employ[item]


c = Company(['张三', '李四'])
print('公司人数{}'.format(len(c))
for person in c:
    print(person)

(5)继承中可能出现的属性值陷阱问题,子类和父类中同名属性会相互影响。

class P(object):

    def __init__(self):
        self.value = 0
        print('父类初始化时,该属性的值:{}, id:{}'.format(self.value, id(self.value)))

    def get(self):
        # 父类中修改,子类也会受影响
        self.value = 10
        print('调用父类get,该属性的值:{}'.format(self.value))
        print('调用父类get,该属性的id:{}'.format(id(self.value)))


class C(P):

    def __init__(self):
        # 先调用父类构造函数。父类构造函数中的属性在子类中可以使用
        super(C, self).__init__()
        print('直接从父类继承来的属性值:{}, id:{}'.format(self.value, id(self.value)))
        # 这一句会覆盖父类中同名属性,且父类的的属性也会相应更改
        self.value = 100

        print('子类初始化后,该属性的值:{}, id:{}'.format(self.value, id(self.value)))

    def get(self):
        print('调用子类get,该属性的值{}'.format(self.value))
        print('调用子类get,该属性的id{}'.format(id(self.value)))

    def get_super(self):
        return super(C, self).get()


c = C()
c.get_super()
c.get()


# 输出:父类初始化时,该属性的值:0, id:140711492811808
       直接从父类继承来的属性值:0, id:140711492811808
       子类初始化后,该属性的值:100, id:140711492815008
       调用父类get,该属性的值:10
       调用父类get,该属性的id:140711492812128
       调用子类get,该属性的值10
       调用子类get,该属性的id140711492812128

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值