Python之——面向对象编程

本次博客参考廖雪峰老师官网内容,介绍Python面向对象编程。涵盖类、实例和类的方法,属性访问限制,继承和多态等内容。还提及实例和类属性区别、动态绑定方法、高级特性如@property装饰器,以及三种定义类方法的方式。

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

        本次内容主要参考廖雪峰老师官方网站内容,有不清楚遗漏地方请参考廖雪峰老师官网内容。

1. 类、实例和类的方法

面向对象编程通俗理解:

  • 面向过程:自己靠双手按照流程解决问题
  • 面向对象:找个对象,让她做,自己轻松了
  • 对象是对客观事物的抽象,类是对对象的抽象
  • 对象是类的实例,类是对象的模板
  • 类的描述:类名,属性,方法
  • 类名:类的名字
  • 属性:类的属性,可以认为是在类里对象里定义的变量
  • 方法:类的方法,是def 定义的,可以理解为函数

(1)类:类是创建实例的模板,定义一个类是通过class关键字;
(2)实例:是根据类创建出来的一个个具体的对象,各个实例拥有的数据都互相独立,互不影响;
(3)方法:就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据;
封装数据的函数(访问数据的函数)是和类本身关联起来的,称为类的方法;
每个对象都拥有相同的方法,但各自的数据可能不同

# 定义类Student
class Student(object):
    pass

# 创建实例,根据student类创建出student的实例
bart = Student()

# 给实例变量绑定属性
bart.name = 'bank'

# 在创建实例时,通过__init__方法把必需的属性绑定上去,第一个参数永远是self,它表示创建的
# 实例本身,可以把各种属性绑定到self
class Student(object):

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


# 类内部定义访问数据的函数,封装数据的函数和类本身关联起来就称为类的方法
class Student(object):

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

    def print_score(self):
        print("%s,%s" % (self.name, self.score))

    def get_grade(self):
        if self.score >= 90:
            return 'A'
        elif self.score >= 60:
            return 'B'
        else:
            return 'C'

lisa = Student('Lisa', 99)
bart = Student('Bart', 59)
print(lisa.print_score(), lisa.get_grade())
print(bart.print_score(), bart.get_grade())

结果如下:
Lisa,99
None A
Bart,59
None C

2. 属性的访问限制

#如果要让内部属性不被外部访问,就在属性的名称前加上两个下划线__,实例的变量名如果以__开头
#,就变成了一个私有变量(private),只有内部可以访问,外部不能访问
class Student(object):

    def __init__(self, name, score):
        self.__name = name
        self.__score = score

    def print_score(self):
        print('%s: %s' % (self.__name, self.__score))

bart = Student('Bart Simpson', 59)
print(bart.__name)
# 结果如下:(外部访问内部属性就会报错如下)
# AttributeError: 'Student' object has no attribute '__name'

# 如果外部代码要获取name和score,需要给Student类增加get_name和get_score这样的方法:
    def get_name(self):
        return self.__name

    def get_score(self):
        return self.__score

# 如果又要允许外部代码修改score,就需要再给Student类增加set_score方法
    def set_score(self, score):
        self.__score = score

需注意:在Python中,变量名类似__xxx__的,是特殊变量,特殊变量是可以直接访问的
,不是private变量,所以不能用__name__、__score__这样的变量名作为属性命名。
一个下划线开头的实例变量名,比如_name这样的实例变量外部是可以访问的,但是尽量不要随意访问
# 小习题:
# 将student类的gender字段对外隐藏,且使外部又可以访问和修改
class Student(object):

    def __init__(self, name, gender):
        self.__name = name
        self.__gender = gender

    def get_gender(self):
        return self.__gender	

    def set_gender(self, gender):
        self.__gender = gender


# 测试:
bart = Student('Bart', 'male')
if bart.get_gender() != 'male':
    print('测试失败!')
else:
    bart.set_gender('female')
    if bart.get_gender() != 'female':
        print('测试失败!')
    else:
        print('测试成功!')
# 答案:测试成功!

3. 继承和多态

继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。

# 继承
class Animal(object):

    def run(self):
        print("animal is runing.....")

# Animal是父类,Cat/Dog是Animal子类,即:object -> Animal -> Dog/Cat
class Cat(Animal):
    pass

class Dog(Animal):
    pass
a = Animal()
dog = Dog()
print(dog.run())
cat = Cat()
print(cat.run())

# 多态
class Dog(Animal):

    def run(self):
        print('Dog is running...')

class Cat(Animal):

    def run(self):
        print('Cat is running...')
b=Dog()
print(b.run())
c=Cat()
print(c.run())

print(isinstance(b, Dog))
print(isinstance(b, Animal))
print(isinstance(c, Cat))
print(isinstance(c, Animal))
print(isinstance(a, Dog))
# 在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。
# 但反过来就不行

结果如下:
animal is runing…
animal is runing…
++++++++++++++++++++++++++++++++++++
Dog is running…
Cat is running…
++++++++++++++++++++++++++++++++++++
True
True
True
True
False
#####小插曲之——获取对象信息
(1)基本类型判断用:type()
(2)若判断一个对象是否是函数:types()模块中定义的常量
(3)若判断class类型:isinstance()函数,当然isinstance函数也可以判断其他基本类型,还可以判断一个变量是否是某些类型中的一种,切记:总是优先使用isinstance()判断类型。
(4)若要获得一个对象的所有属性和方法使用:dir()函数,还可以配合getattr()、setattr()以及hasattr()函数使用,直接操作一个对象的状态

# type()及isinstance()使用
print(type(123),'\t',type('asd'),'\t',type(abs))
print(type('123')==str,'\t',type(123)==type('123'),'\t',type('asd')==type('123'))
print(isinstance([1, 2, 3], (list, tuple)))

# types模块使用
import types
def fn():
	pass
print(type(fn),'\t',type(fn)==types.FunctionType,'\t',type(abs)==types.BuiltinFunctionType)

# 结果如下:
# <class 'int'>   <class 'str'>   <class 'builtin_function_or_method'>
# True    False    True
# True
# <class 'function'>   True    True

################################################################################

# dir的使用及获取具体属性及方法
class myobject(object):

    def __init__(self):
        self.x = 9

    def power(self):
        return self.x * self.x

print(dir(myobject)) #获取myobject类的所有属性及方法
obj = myobject()
print(hasattr(obj, 'x'), '\t', hasattr(obj, 'y'))  # 判断是否含有某属性
setattr(obj, 'y', 20)  # 设置一个属性
print(hasattr(obj, 'y'), '\t', getattr(obj, 'y'))  # 获取某属性
print(hasattr(obj, 'power'),'\t',getattr(obj, 'power')) #获取对象的方法
print(obj.power())

# 结果如下:
# ['__class__', '__delattr__', '__dict__', '__dir__', 。。。。。']
# True 	 False
# True 	 20
# True 	 <bound method myobject.power of <__main__.myobject object at 0x00000000028CE278>>
# 81

4. 实例属性和类属性

实例属性属于各个实例所有,互不干扰;
类属性属于类所有,所有实例共享一个属性;
不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误。

# 类属性\实例属性
class Student(object):
    name = 'Student' #类属性
s=Student()
print(s.name)       # 因为实例并没有name属性,所以会继续查找class的name属性
print(Student.name) # 打印类的name属性
s.name = 'Michael'  # 给实例绑定name属性
print(s.name)       # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
del s.name          # 如果删除实例的name属性
print(s.name)       # 由于实例的name属性没有找到,类的name属性就显示出来了

结果如下:
Student
Student
Michael
Student

# --------小练习-------
class Student(object):
    count = 0  # 类属性

    def __init__(self, name):
        self.name = name
        Student.count += 1

# 测试:
if Student.count != 0:
    print('测试失败!')
else:
    bart = Student('Bart')
    if Student.count != 1:
        print('测试失败!')
    else:
        lisa = Student('Bart')
        if Student.count != 2:
            print('测试失败!')
        else:
            print('Students:', Student.count)
            print('测试通过!')

5. 动态绑定方法到类

创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性

# 定义一个Student类
class Student(object):
    pass

s = Student
s.name = 'jeck'  # 动态给实例绑定一个属性
print(s.name)
# 打印出:jeck

def set_age(self, age):  # 定义一个函数作为实例方法
    self.age = age

from types import MethodType
s.set_age = MethodType(set_age, s)  # 动态给实例绑定一个方法
s.set_age(26)  # 调用实例方法
print(s.age)
# 打印出:26

# 动态绑定是在程序运行的过程中动态给class加上方法
Student.set_age = set_age  # 给class动态绑定一个方法
s2 = Student()  # 实例化
s2.set_age(30)  # 调用实例方法
print(s2.age)
# 打印出:30


# 动态绑定过程中限制所绑定实例的属性
# 定义class的时候,定义一个特殊的__slots__变量,限制该class实例能添加的属性
class Student(object):
    __slots__ = ('name', 'age')  # 用tuple定义允许绑定的属性名称

s = Student()
s.name = 'jaspuer'
s.age = 26
s.score = 100  # 此句报错,由于class定义时score没有被放入__slots__中
# 注意:__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用

6. 高级特性

####(1)@property装饰器
Python内置的@property装饰器就是负责把一个方法变成属性调用
既能检查参数正确性,又可以用类似属性这样简单的方式来访问类的变量

class Student(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0~100')
        self._score = value

s = Student()
s.score = 60  # 实际转化为s.set_score(60)
print(s.score)  # 实际转化为s.get_score()
# 结果为:60

# 定义只读属性
class Student(object):

    @property
    def birth(self):
        return self._birth
    
    # birth是可读写属性
    @birth.setter
    def birth(self, value):
        self._birth = value

    # age就是一个只读属性
    @property
    def age(self):
        return 2015 - self._birth

7. 三种定义类方法的方式

常规方式, @classmethod修饰方式, @staticmethod修饰方式

class A(object):
    def foo(self, x):
        print("executing foo(%s,%s)" % (self, x))
        print('self:', self)
    @classmethod
    def class_foo(cls, x):
        print("executing class_foo(%s,%s)" % (cls, x))
        print('cls:', cls)
    @staticmethod
    def static_foo(x):
        print("executing static_foo(%s)" % x)    

普通的类方法foo()需要通过self参数隐式的传递当前类对象的实例
@classmethod修饰的方法class_foo()需要通过cls参数传递当前类对象
@staticmethod修饰的方法定义与普通函数是一样的
self和cls的区别不是强制的,只是PEP8中一种编程风格,slef通常用作实例方法的第一参数,cls通常用作类方法的第一参数。即通常用self来传递当前类对象的实例,cls传递当前类对象。

示例:

class cal:
    cal_name = '计算器'
    def __init__(self,x,y):
        self.x = x
        self.y = y

    @property #在cal_add函数前加上@property,使得该函数可直接调用,封装起来
    def cal_add(self):
        return self.x + self.y

    @classmethod  
    #在cal_info函数前加上@classmethon,则该函数变为类方法,
    #该函数只能访问到类的数据属性,不能获取实例的数据属性
    def cal_info(cls):  #python自动传入位置参数cls就是类本身
        print('这是一个%s'%cls.cal_name)   #cls.cal_name调用类自己的数据属性

    @staticmethod       #静态方法 类或实例均可调用
    def cal_test(a,b,c): #改静态方法函数里不传入self 或 cls
        print(a,b,c)
c1 = cal(10,11)
cal.cal_test(1,2,3)     #>>> 1 2 3
c1.cal_test(1,2,3)      #>>> 1 2 3
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值