python面向对象详解

什么是面向对象?

面向对象是一种编写代码的思维方式:程序是由什么构成的。对于面向对象,肯定是基于类进行编码,与之前基于过程进行编码相对。

什么是类?

所谓类就是对客观世界一类事物的抽象化,把同一类事物的共有属性,共有行为抽取出来。例如:床类,都有长宽高属性,有用于提供睡觉场所的作用。这里的作用用类里面的方法来代替。属性表示类的特点,方法表示类的功能。通常类就是由属性和方法组成。

什么是类的实例化?

就是在某一类中找出一个具体的个体,如你家里那张特定的席梦思床,就是床类中一个特定的个体。在床类中找出你这张床的过程,就是类的实例化。而你的这张床通常也称为类的一个对象。

类的声明

通常类的简单声明如下:

class Cat:
    # 分配内存空间
    def __new__(cls, *args, **kwargs):
        print('___new___')
        return super().__new__(cls)
    # 构造方法,该方法在创建对象(实例)的时候在,会自动调用
    def __init__(self, color, name):
        """函数在类里面称为方法,self指的是当前对象
        即调用方法的对象
        """
        self.color = color
        self.name = name

    def catch_Rat(self):
        print(self.name + '抓到了老鼠')

如上,定义一个Cat类, 其中new方法通常不改写该方法应当省略,这个方法是为了系统分配内存空间,如不写,系统也会默认创建调用该方法。而init方法相当于c语言的构造函数。其方法第一个参数self是系统默认的名称,不会初始化成类对象的属性。self起的作用是后面进行类的实例化时,表示进行实例化的对象,以初始化对象的属性,后面的color,name均为有效参数,用于赋值

对象成员

凡是通过init函数所声明的成员(也叫属性)均属于对象的属性,不属于类的属性,因为同一类事物不同对象个体间属性会有所不同,用于区分个体。如下:

class Cat:
    # 构造方法,该方法在创建对象(实例)的时候在,会自动调用
    def __init__(self, color, name):
        """函数在类里面称为方法,self指的是当前对象
        即调用方法的对象
        """
        self.color = color
        self.name = name
littleCat = Cat('white', 'nasi')
print(littleCat.color, littleCat.name)

运行效果如下:

white nasi

在初始化时,littleCat = Cat('white', 'nasi')语句调用了init函数。其中'white'和'nasi'分别传递给init函数的第二个和第三个参数,而第一个参数’self‘指的就是这里的littleCat对象,当然self也可以改成其他任何名字,只不过这里大家已经习惯了用self,是一种约定俗成的用法。

类成员

前言引出:
根据上面代码,我们会想,有些属性是所有类的对象都是完全一样的,比如正常情况下,只要是猫都有四条腿,我们总不可能每次声明一个对象都要往init函数里面加一个参数4吧?这样的话每次初始化岂不是还要写成littleCat = Cat('white', 'nasi', 4),每次声明对象都要加个4,很麻烦,可不可以简单一点,让接littleCat = Cat('white', 'nasi'达到相同的效果?当然可以,查看第一种:

class Cat:
    def __init__(self, color, name):
        self.color = color
        self.name = name
        self.footNumber = 4

littleCat = Cat('white', 'nasi')
print(littleCat.color, littleCat.name,littleCat.footNumber)

white nasi 4

这样当然可以实现我们想要的要求,但我们仔细观察会发现,其实这没有多大意义,因为footNumber所有对象的值都完全相同,他应该是所有猫的属性,而不只是这一个猫,所以引出了类成员。如下写更合适:

class Cat:
    footNumber = 4
    def __init__(self, color, name):
        self.color = color
        self.name = name

littleCat = Cat('white', 'nasi')
print(littleCat.color, littleCat.name, Cat.footnumber)

white nasi 4

类成员应该用类名.属性名来访问。此时类变量是所有对象共享,用类名.属性名更改类变量后,所有对象会同步更改,但一旦使用了对象名.类变量,则该变量在该对象中变成了对象变量了,不再与其他对象的该属性同步了,此时类和该对象可以看成两个不同的对象,各自访问自己的。
如下:

class Cat:
    count = 0
    def __init__(self, color, name):
        self.color = color
        self.name = name

cat1 = Cat('white', 'nasi')
print(Cat.count)

Cat.count += 1
print(cat1.count)
print(Cat.count)

cat1.count += 1
print(cat1.count)
print(Cat.count)

cat2 = Cat('black', 'qiongsi')
print(Cat.count)
print(cat2.count)

#
# print(Cat.footNumber)
# Cat.footNumber += 1
# print(Cat.footNumber)

0
1
1
2
1
1
1

类变量的用途举例,可以用于统计创建的对象个数。

类方法

类中方法的创建同c++,调用方法类似于调用成员,用对象.函数()即可。如下:

class Cat:

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

    def catch_Rat(self):
        print(self.name + '抓到了老鼠')

cat = Cat('white', 'nasi')

cat.catch_Rat()

nasi抓到了老鼠

方法在类里面创建时,会自动生成第一个参数,名为self,指向调用次方法的对象,该参数强制生成。所以,类中的方法至少有一个参数self,当然,self也可以更名为其他名字。

属性私有化

  1. 用双下划线开头
    双下划线开头之所以能够隐藏变量,是因为双下划线能够自动更改原属性名为“_类名__属性名”。调用方法'.__dict__'即可看出。
class Cat:

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

    def catch_Rat(self):
        print(self.__name + '抓到了老鼠')

cat = Cat('white', 'nasi')
print(cat.__dict__)

{'color': 'white', '_Cat__name': 'nasi'}

此时name属性被隐藏,既不能用cat.name调用,也不能用cat.__name调用。如下:

class Cat:

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

    def catch_Rat(self):
        print(self.__name + '抓到了老鼠')

cat = Cat('white', 'nasi')
print(cat.name)
print(cat.__name)

AttributeError: 'Cat' object has no attribute 'name'

但是,当我们知道了隐藏了变量的规则后,我们仍然可以通过cat._Cat__name来调用。

class Cat:

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

    def catch_Rat(self):
        print(self.__name + '抓到了老鼠')

cat = Cat('white', 'nasi')

print(cat._Cat__name)

nasi

私有变量部分开放

可以在类里面声明一个调用私有变量的函数,外部要调用私有变量时,就通过调用函数,间接调用私有变量,实现私有变量的有限开放。如下:

class User:
    def __init__(self, name):
        self.__name = name

    def get_name(self):
        return self.__name

li = User('li')
print(li.get_name())

li

但这样总感觉每次调用属性有点麻烦,所以可以考虑语法糖修饰器@。所谓修饰器就是:

在Python的函数中偶尔会看到函数定义的上一行有@functionName的修饰,当解释器读到@的这样的修饰符之后,会先解析@后的内容,直接就把@下一行的函数或者类作为@后边的函数的参数,然后将返回值赋值给下一行修饰的函数对象。
比如:
@a
@b
def c():

python会按照自下而上的顺序把各自的函数结果作为下一个函数(上面的函数)的输入,也就是a(b(c()))

所以接下来介绍几个在类中常用的修饰器

  • @property 返回其后第一个函数的返回值
class User:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    @property
    def name(self):
        return self.__name

    @property
    def age(self):
        return self.__age

    @property
    def test(self):
        return 1

li = User('li', 18)
print(li.age)
print(li.name)
print(li.test)

18
li
1

其实这里property的作用就是把下面的函数装饰成一个属性。

  • @property.setter 对property装饰器后面函数返回的属性进行赋值
    要求:setter前的property与property装饰器后的函数名,以及setter装饰器后面的函数名都要一致。使用setter时必须先有property。
class User:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    @property
    def name(self):
        return self.__name

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, age):
        self.__age = age
        print('赋值成功')
    def get_age(self):
        return self.__age


li = User('li', 18)
li.age = 15
print(li.get_age())

赋值成功
15

这里实现了对私有属性的赋值操作

  • @classmethod
    修饰的方法是类方法,类方法可以用类名调用,且必须有一个类型参数,如下面的cls,cls指的就是该类,类型参数可以任意命名,在其下的函数创建对象时可以增加属性
class User:
    def __init__(self, name):
        self.name = name

    @classmethod
    def create_user(cls, name, age):
        print(cls)
        user = cls(name)
        user.name = name
        user.age = age
        return user
li = User.create_user('li', 18)
print(li.name)
print(li.age)
print(li)

main.User'>
li
18
main.User'>
<main.User object at 0x000001FEF4C9A160>

这里实现了新建一个对象,而且这个对象比原类多了一个age属性

  • @staticmethod
    该装饰器装饰的方法会变成静态方法,可以通过类名直接调用,常用于写工具类
class User:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    @staticmethod
    def sum(a,b):
        return a+b

print(User.sum(1, 2))

3

类中的str方法

正常情况下直接打印对象返回的是对象的内存地址,若类中存在该方法,打印类的对象时会自动执行该方法。所以通常在该方法中执行一些语句,打印出更多关于该对象的信息

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

    @classmethod
    def create_user(cls, name, age):
        print(cls)
        user = cls(name)
        user.name = name
        user.age = age
        return user
    def __str__(self): # 类中若是存在该函数,打印类对象的时候会自动执行该函数
        _str = ''
        for k,v in self.__dict__.items():
            _str += str(k)
            _str += ':'
            _str += str(v)
            _str += ','
        print('__str__')
        print(_str)
        return _str
li = User.create_user('li', 18)
print(li)

main.User'>
str
name:li,age:18,
name:li,age:18,

继承

所有的继承不指定父类都默认指定object,python 支持多继承,继承内容与顺序相关,所有的类,都会默认继承object类,继承通常会继承父亲的所有方法和子类,也可以子类重写

  • 单继承
class A:
    def __init__(self):
        self.name = 'zs'
    def print_test(self):
        print('AAAAAAAAAAAAA')

class C(A):
    def __init__(self):
        super().__init__()  # 先调用父亲的初始化方法,super()表示调用的是父亲的
        # 若删除该语句,则应该c里面的初始化方法,可能导致父类部分属性消失
        self.age = 20

test = C()
print(test.name, test.age)

zs 20

子类继承了父类的name属性,同时自己新增了age属性。

  • 多继承
class A:
    def __init__(self):
        self.name = 'zs'
    def print_test(self):
        print('AAAAAAAAAAAAA')

class B:
    def __init__(self):
        self.name = 'B'
    def print_test(self):
        print('BBBBBBBBBBB')

class D(B,A):
    name = 'D'
    def __init__(self):
        super().__init__()
        self.age = 21
    def print_test(self):
        super().print_test()
        print('DDDDDDDDDD')

test = D()
print(test.name)
test.print_test()

B
BBBBBBBBBBB
DDDDDDDDDD

通过观察,我们发现他是按照顺序继承的,查看调用属性或方法的查找顺序:print(类名.mro),先自己,然后第一个,第二个。。

组合

在编程时,组合优于继承,但是效果相同,所谓组合,就是类的嵌套(个人理解),如下:

class A:
    def __init__(self):
        self.name = 'zs'
    def print_test(self):
        print('AAAAAAAAAAAAA')

class E:
    def __init__(self):
        self.a = A()
    def print_test(self):
        self.a.print_test()
        print("DDDDDDDDDD")

test = E()
print(test.a.name)
test.print_test()

zs
AAAAAAAAAAAAA
DDDDDDDDDD

编程常用技巧:鸭子类型

简单来说,就是用不同的类给同一个变量赋值,这些类之间有同名的方法,用变量.方法调用时,变量是用哪个类的对象就调用哪个类里面的方法。这样可以优化程序,可读性也更高。

class Programer:
    def dowork(self):
        print("biancheng")
class Manger:
    def dowork(self):
        print("guanli")

employee = Manger()
employee.dowork()
employee = Programer()
employee.dowork()

类的单例

单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。这里我们讲解一个单例模式的实现方法——通过重写系统创建对象时分类内存的函数new

class S:
    instance = None
    # __new__方法用来制作单例
    def __new__(cls, *args, **kwargs):
        if S.instance == None:
            S.instance = super().__new__(cls) # 这句话是用来分配内存的,
        return S.instance
s1 = S()
print(id(s1))
s2 = S()
print(id(s2))

2159709448232
2159709448232

你可能感兴趣的:(python面向对象详解)