python面向对象程序设计三大特点:封装、继承、多态、类方法@classmethod、类属性、静态方法@staticmethod、object类

2023-11-06

注意:

1、类的私有属性、私有方法,在类的外部对象(也就是类的实例)是无法访问的;如果要访问,可以在类的内部,再定义一个方法让它去访问私有属性和私有方法,然后在类的外部通过调用这个方法,就能轻松访问到类的私有属性和私有方法了。

2、当形参有缺省值时(也叫默认值),不传实参,那么形参就用默认值。

如果要传实参:

(1)当实参个数 < 形参个数时,如果实参的值不是传给第一个形参,那么实参在写的时候就要写上对应的形参名。因为默认会传给第一个,然后依次往下排。

(2)当实参个数 = 形参个数时,不用写形参名。

3、普通方法能调用类属性、类方法,而类方法不能调用普通方法和普通属性。

4、无论是在类里的方法嵌套,还是一般程序中的函数嵌套,如果有两个函数:第一个函数中有return;第二个函数是直接调用第一个函数,此时,执行第二个函数,结果是控制台上啥都没有。例如:

def func1():
    a = 1
    b = 2
    return a + b

def func2():
    func1()

func2()  # 调用函数2、运行函数2
结果:

进程已结束,退出代码 0

 你可能会想,func1里带return,那我加个print就好了,结果:却返回的是None。答案还是不对。

print(func2())
结果:
None

 我们想要的是让它返回3,但是却是None,说明,还是没有拿到函数1的返回值。所以需要把函数2进行改进,把它改成return func1(),这样再运行就是我们想要的结果了。

def func1():
    a = 1
    b = 2
    return a + b

def func2():
    return func1()  # 返回调用函数1的结果

print(func2())
结果:
3

小结:当发生函数嵌套时,如果被调用的函数是带return,那么另个函数在调用的时候,前面一定要加return。

5、如果类中定义的属性是个常量,那怎么修改这个属性的值?为了看的更清楚,这里将对:类属性和普通属性分别进行演示。

首先是对类属性的值进行修改:4种方法。

class Dog:
    name = "大黄"  # 类属性

#法1
Dog.name = "二哈"  # 类名.类属性名直接修改为“二哈”
print(Dog.name) #输出修改后的类属性值
结果:
二哈


#法2:通过类方法中的形参来进行修改

class Dog:
    name = "大黄"  # 类属性

    @classmethod  # 类方法
    def set_name(cls, name):
        cls.name = name  # 通过形参来修改类属性name的值
        print(cls.name)  # 输出修改后类属性的值

Dog.set_name("二哈")
结果:
二哈


# 法3 在对象访问时进行修改
class Dog:
    name = "大黄"  # 类属性


d = Dog()  # 创建类的实例
d.name = "二哈"  # 用对象去修改类属性的值
print(d.name)  # 输出修改后的值
结果:
二哈


# 法4 通过普通方法中的形参来进行修改
class Dog:
    name = "大黄"  # 类属性

    def demo(self, a):  # a为demo的形参
        Dog.name = a   # 形参值赋给类属性。注意:普通方法中调用类属性:类名.类属性名
        print(Dog.name)


d = Dog()
d.demo("二哈")  # 调用普通方法,实参为二哈
结果:
二哈

小结:修改类属性的值,总的来说就两种方式要么在类的内部进行修改要么在类的外部进行修改如果是在类的内部,那么类方法和普通方法都能调用类属性,所以从这就能衍生出来两种方法。而如果是在类的外部,那么又有两种访问方式:类名.类属性名  或者  对象名.类属性名,这样又能衍生出来两种方法,所以一共四种。

对普通属性的值进行修改:一种方法。即:直接通过对象名.属性名=值的方式进行修改也就是法1。下面的法2 其实就是把属性定义成变量,而不是对常量进行修改。要是定义成变量,那就有两种方式,例如下面的法2和法3 。

# 法1 :在对象中进行修改————推荐
class Dog:

    def __init__(self):
        self.age = 5  # 普通属性,这是个常量


d = Dog()  # 类的实例
d.age = 10  # 修改属性的值
print(d.age)   # 输出属性的值

结果:
10

#法2本质上就是把属性设为变量。
# 法2 通过方法的形参来修改——不推荐,这种的话,init初始化方法就没必要写了
class Dog:
    def __init__(self):
        self.age = 5  # 普通属性为常量

    def set_age(self, age):  # 普通方法,age为形参
        self.age = age  # 将形参age的值赋给属性name
        print(self.age)  # 其实这种写法本质上,也是定义属性name,与上面的init一样


d = Dog()
d.set_age(10)  # 调用方法,10为实参
结果:
10

# 改进——去掉多余的init方法
class Dog:
    def set_age(self, age):  # 普通方法,age为形参
        self.age = age  # 将形参age的值赋给属性name
        print(self.age)  # 其实这种写法本质上,也就是定义属性name


d = Dog()
d.set_age(10)  # 调用方法,10为实参
结果:
10

# 法3——把属性定义成变量
class Dog:
    def __init__(self, age):
        self.age = age

    def __str__(self):  # str方法返回的必须是字符串类型,所以这里把age的int改为str
        return str(self.age)


d = Dog(10)
print(d)
结果:
10

6、静态方法不能访问类里面的其它成员,但类里面的其它成员却可以访问它。

一、封装

封装就是把类的属性和方法通过两个下划线封装到类的内部,完了只能在类的内部使用它不能在类的外部使用。这就叫封装。

也就是说,把类的属性和方法前面加俩下划线,让他们变成类是私有属性和私有方法,完了只能在类的内部使用,不能在类的外部使用,这就叫封装。

具体做法:在属性名或方法名前面加两个下划线,这个属性和方法就变成了类的私有属性和私有方法。此时,它俩就只能在类的内部访问和调用,在类的外部就不能用了。

私有属性:就是类不希望公开的属性。

私有方法:就是类不希望公开的方法。

例如:定义一个User类,它有一个非私有属性name和一个非私有方法shou_name,以及一个私有属性密码和一个私有方法__show_passwd。

# 访问类的非私有属性
class User:
    def __init__(self):
        self.name = "小花"  # 把属性name设置为常量
        self.__passwd = "123"  # __passwd是一个私有属性

    def show_name(self):  # 显示名字
        print(self.name)

    def __show_passwd(self):  # 定义一个私有方法
        print(self.__passwd)


a = User()
a.show_name()
结果:
小花


# 此时想要访问它的私有属性——密码,如果还用之前的对象名.方法名的方式去访问,会发现报错,无法访问
class User:
    def __init__(self):
        self.name = "小花"  # 把属性name设置为常量
        self.__passwd = "123"  # __passwd是一个私有属性

    def show_name(self):  # 显示名字
        print(self.name)

    def __show_passwd(self):  # 定义一个私有方法
        print(self.__passwd)


a = User()
a.__show_passwd()
结果:
    a.__show_passwd()
AttributeError: 'User' object has no attribute '__show_passwd'

那怎么访问类的私有属性和私有方法呢?很简单,我们可以在类的内部定义一个方法让它专门用来访问类的私有属性和私有方法然后在类的外部再调用这个方法即可。

例如:定义一个demo方法,让它用来访问私有属性和私有方法。

class User:
    def __init__(self):
        self.name = "小花"  # 把属性name设置为常量
        self.__passwd = "123"  # __passwd是一个私有属性

    def show_name(self):  # 显示名字
        print(self.name)

    def __show_passwd(self):  # 定义一个私有方法
        print(self.__passwd)

    def demo(self):  # 定义一个方法,用来访问类的私有属性和私有方法
        print(self.__passwd)  # 访问类的私有属性
        self.__show_passwd()  # 调用类的私有方法


a = User()
a.show_name()
a.demo()
结果:
小花
123
123

观察上面代码可以看到,两个属性用的都是常量,这样的结果是,无论哪个对象调用都是显示,小花和123,因此,为了增加代码的灵活性,现在把它改为变量。即:在初始化方法中加形参

例如:把name、passwd这两个属性写成变量。

class User:
    def __init__(self, name="小花", passwd="123"):
        self.name = name  # 把属性name设置为【变量】
        self.__passwd = passwd  # 把__passwd也设置为【变量】

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

    def __show_passwd(self):  # 定义一个私有方法
        print(self.__passwd)

    def demo(self):  # 定义一个方法,用来访问类的私有属性和私有方法
        print(self.__passwd)  # 访问类的私有属性
        self.__show_passwd()  # 调用类的私有方法


a = User()   # 实例化时候括号里面没加实参,那么,名字和密码都将使用默认值。
a.show_name()
a.demo()
结果:
小花
123
123

# 实例化时候,括号里加实参,此时,名字和密码将用实参的值
b = User("张三", "aasd")
b.show_name()
b.demo()
结果:
张三
aasd
aasd

当然,实例化时候,实参的个数也可以不写全。

例如:分别只传一个实参。

class User:
    def __init__(self, name="小花", passwd="123"):
        self.name = name  # 把属性name设置为【变量】
        self.__passwd = passwd  # 把__passwd也设置为【变量】

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

    def __show_passwd(self):  # 定义一个私有方法
        print(self.__passwd)

    def demo(self):  # 定义一个方法,用来访问类的私有属性和私有方法
        print(self.__passwd)  # 访问类的私有属性
        self.__show_passwd()  # 调用类的私有方法

b = User("李四")
b.show_name()
b.demo()
结果:

李四
123
123


b = User("YYDS")  # 本来是要把YYDS传给密码,但是因为没写形参名,导致传给了name
b.show_name()
b.demo()
结果:
YYDS
123
123

 # !!当有多个形参时,如果要传递的形参不是排第一个,
# 那么实参在写的时候要加上对应的形参名,否则,默认都将传给第一个。
b = User(passwd="YYDS")  # 密码在形参中排第二个,所以实参在写的时候,一定要加上形参名
b.show_name()
b.demo()
结果:
小花
YYDS
YYDS

需要注意的是,当有多个形参时,要传递的实参,如果不是传给第一个形参,那么实参在写的时候一定要加上对应的形参名字,否则会默认把它传给第一个形参。例如,上面代码中,如果要把YYDS这个实参传递给密码,那么实参在写的时候,就要写成,passwd="YYDS",否则就会传给第一个形参name了。当然,如果实参个数与形参个数一致,那么就不用写形参名了。

b = User("张三", "YYDS")  
b.show_name()
b.demo()

结果:
张三
YYDS
YYDS

二、继承

主要用来实现代码的复用,相同的代码不需要重复的去编写。

语法:

class 父类:

        pass

class 类名(父类名):

        pass

例如:

class Animal:
    pass


class Dog(Animal):  # 狗继承自动物
    pass

解析:子类也叫派生类、父类也叫基类、继承也叫派生

例如上面这段代码:狗是动物的子类,又可以说,狗是动物的派生类。

动物是狗的父类,又可以说,动物是狗的基类。

狗继承于动物,狗派生于动物。

注意:

(1)子类继承父类之后,子类就拥有了父类所有的方法和属性(除了父类的私有属性、私有方法,无法继承)。

例如:Dog类继承自Animal类,此时,Dog类就有三种方法。

class Animal:
    def sleep(self):
        print("睡觉")

    def eat(self):
        print("吃东西")


class Dog(Animal):  # Dog类继承自Animal类,此时,Dog类就有三种方法
    def run(self):
        print("跑")
d = Dog()
d.sleep()
d.eat()
d.run()

结果:
睡觉
吃东西
跑

(2)父类中所有的私有属性、私有方法归父类所有,子类不能继承。能继承的都是公有属性和公有方法。

class Animal:
    def __init__(self, name="豆豆"):
        self.__name = name  # 父类私有属性

    def __eat(self):  # 私有方法不会被子类继承
        print("%s在吃东西" % self.__name)


class Dog(Animal):
    pass


d = Dog()
d.__eat()

结果:
    d.__eat()
AttributeError: 'Dog' object has no attribute '__eat'

那怎么访问父类的私有属性和私有方法呢可以通过父类的公有方法来间接的访问到父类私有属性和私有方法。  

class Animal:
    def __init__(self, name="豆豆"):
        self.__name = name

    def __eat(self):  # 私有方法不会被子类继承
        print("%s在吃东西" % self.__name)

    def demo(self):  # 利用demo这个公有方法,来间接的访问私有方法和私有属性
        self.__eat() # 访问私有方法


class Dog(Animal):
    pass


d = Dog()
d.demo()  #调用demo

结果:
豆豆在吃东西

(3)所谓继承,一定是在有可继承关系的两个事物之间。比如,动物和狗。而没有关系的两个事物之间是不存在继承的,比如,植物和狗。这俩都没啥联系,也就没办法继承。

(4)继承是单向的,子类能拥有父类的属性和方法,而父类却不能拥有子类的属性和方法。也就是说,父类里不能访问(调用)子类中特有的属性和方法

例如:父类访问子类中的run方法,结果报错。

class Animal:  # 父类Animal
    def eat(self):
        print("吃")


class Dog(Animal):  # 子类1
    def run(self):
        print("能跑能跳")


a = Animal()  # 为父类创建一个对象
a.run()  #父类调用子类的方法——报错,说明,继承是单向的
结果:
    a.run()
AttributeError: 'Animal' object has no attribute 'run'

(5)一个父类可以有多个子类。每个子类也可以拥有自己特有的属性和方法。

例如:Animal这个父类,可以有三个子类,而这三个子类中,继承父类方法的同时,又各自拥有自己的其它方法。

class Animal:  # 父类Animal
    def eat(self):
        print("吃")


class Dog(Animal):  # 子类1
    def run(self):
        print("能跑能跳")


class Bird(Animal):  # 子类2
    def fly(self):
        print("能飞")


class Fish(Animal):  # 子类3
    def swimming(self):
        print("能河里游")


d = Dog()  # 创建一个对象
d.eat()
d.run()  # 狗这个子类,所特有的方法

b = Bird()  # 创建一个对象
b.eat()
b.fly()  # 鸟这个子类,所特有的方法

f = Fish()  # 创建一个对象
f.eat()
f.swimming()  # 鱼这个子类,所特有的方法
结果:
吃
能跑能跳
吃
能飞
吃
能河里游

(6)继承的传递性——继承支持多级的继承

例如,类C继承自类B,而类B继承自类A,那么类C,就同时拥有了类A和类B里所有属性和方法。

例如:柯基继承于狗,狗继承于动物,此时,柯基就拥有了狗和动物所有的方法。

class Dog(Animal):  # Dog继承于Animal
    def run(self):
        print("能跑能跳")


class Keji(Dog):  # Keji继承于Dog
    def play(self):
        print("会闹腾")


k = Keji()
k.eat()  # 调用父类的父类的方法
k.run()  # 调用父类的方法
k.play()  # 调用自己的方法

结果:
吃
能跑能跳
会闹腾

再比如,给父类加个属性。

class Animal:
    def __init__(self, name="动物", color="橘黄色"):
        self.name = name
        self.color = color

    def eat(self):
        print("%s的%s在吃东西" % (self.color, self.name))


class Dog(Animal):  # Dog继承于Animal
    def run(self):
        print("能跑能跳")


class Keji(Dog):  # Keji继承于Dog
    def play(self):
        print("%s的%s会闹腾" % (self.color, self.name))  # 调用Animal的属性


k = Keji()
k.eat()  # 调用父类的父类的方法
k.run()  # 调用父类的方法
k.play()  # 调用自己的方法

结果:
橘黄色的动物在吃东西
能跑能跳
橘黄色的动物会闹腾

1、继承之后,方法的重写

所谓方法的重写就是说,当父类的方法不能满足子类的需求时,子类可以对父类的方法进行重写

有两种方式可以实现重写:覆盖父类的方法、扩展父类的方法。

两种方法的异同:

相同点:都是在子类中定义一个和父类同名的方法

不同点:覆盖是直接不要父类的方法了。而扩展是在父类方法的基础上又增加了新功能。

下面分别介绍:

(1)覆盖父类的方法。

当父类的方法完全不能满足子类需求时,可以在子类中,定义一个和父类同名的方法,以此来覆盖父类的方法。这样在运行的时候,调用的就是子类中重写的方法了。

例如:覆盖父类的方法。

class Animal:
    def eat(self):  # 父类中定义了吃
        print("吃")


class Dog(Animal):  # 子类中也定义了吃
    def eat(self):  # 子类中出现了和父类同名的方法,那么父类的eat就会被覆盖
        print("啃骨头")


d = Dog()
d.eat()  # 因为父类的eat方法被覆盖了,所以这里调用的是子类的eat方法

结果:
啃骨头

(2)扩展父类的方法。

当父类方法不能完全满足子类需求时,可以在子类中,定义一个和父类同名的方法,然后在需要的位置上,使用super().同名方法,来调用父类方法。其它位置上再编写自己想要的功能,这样就能在父类方法的基础上增加新的功能。

例如:扩展父类方法。

class Animal:
    def eat(self):
        print("吃")


class Dog(Animal):
    def eat(self):
        print("啃骨头")
        super().eat()  # 在子类方法中,调用父类的eat方法


d = Dog()
d.eat()  # 既执行了父类的eat,又增加了功能
结果:
啃骨头
吃

注意:无论是哪种方式的重写,都是要在子类中定义一个和父类同名的方法,否则就不叫重写,最终调用的依然是父类的方法,而子类的方法不会被调用。

例如:想要重写父类的eat方法,但是子类中却写了个at方法,结果,不会执行子类的方法

class Animal:
    def eat(self):
        print("吃")


class Dog(Animal):
    def at(self):
        super().eat()  
        print("啃骨头")

d = Dog()
d.eat()  # 因为子类里没有eat方法,所以这里调用的是父类的eat方法

结果:
吃

但是,如果想要调用子类的方法怎么办?直接,对象.子类方法名即可。

class Animal:
    def eat(self):
        print("吃")


class Dog(Animal):
    def at(self):
        super().eat()  # 这样虽然也能运行,但是这就不叫重写了,因为子类跟父类都不同名。
        print("啃骨头")  #这里准确的来说应该叫,方法的嵌套

d = Dog()            #即:子类et方法中,调用了父类eat方法
d.at()                #类的内部调用方法,正确的写法是self.方法名
                        #所以super().eat() 应该改为self.eat()
结果:
吃
啃骨头



class Animal:
    def eat(self):
        print("吃")


class Dog(Animal):
    def at(self):
        self.eat()  # 在子类方法中,调用了父类的eat方法
        print("啃骨头")


d = Dog()
d.at()
结果:
吃
啃骨头

三、多态

不同的子类对象,调用相同的父类方法,产生不同的结果,这就是多态。

所谓子类对象,就是对子类创建实例。

多态的前提,不同的子类,来自于同一个父类,子类要覆盖父类的方法(也就是说,子类要重写父类方法)。

例如:狗和牛这两个子类,都调用父类的eat方法,结果却不一样。

class Animal:  # 父类
    def food(self):  # 父类方法
        pass

    def eat(self):  # 父类方法
        self.food()


class Dog(Animal):  # 定义子类,名叫Dog——狗
    def food(self):  # 重写父类方法
        print("狗啃骨头")


class Cow(Animal):  # 定义子类,名叫Cow——牛
    def food(self):  # 重写父类方法
        print("牛吃草")


d = Dog()  # 为Dog创建一个实例
d.eat()  # 子类调用父类方法
c = Cow()  # 为Cow创建一个实例
c.eat()  # 子类调用父类方法
结果:
狗啃骨头
牛吃草

四、类属性、类方法

先梳理下普通属性、普通方法的定义和调用。

普通属性的定义和调用:

定义使用self关键字定义在init初始化方法中。

调用:(1)在类的内部:self.属性名

          (2)在类的外部:先创建类的实例,然后通过对象名.属性名来调用。通常是要结合print函数,因为,对象名.属性名,只是说你拿到了这个值,但是你没输出,控制台就啥都没有,所以,一般都是要写成:print(对象名.属性名)

普通方法的定义和调用:

定义:直接def 方法名即可,方法的第一个形参是self

调用:(1)在类的内部:self.方法名( )

          (2)在类的外部:先创建类的实例,然后通过对象名.方法名( )来调用。注意:如果方法中有return关键字,那就写成:print(对象名.方法名( ))。

1、类属性

定义:直接定义在类里面,即:属性名=值。

调用:(1)在类的内部:如果在普通方法中:类名.类属性名;如果在类方法中,cls.类属性名

           (2)在类的外部:类名.类属性名。通常是要结合print函数,因为,类名.类属性名,只是说你拿到了这个值,但是你没输出,控制台就啥都没有,所以,一般都是要写成:print(类名.类属性名)当然,可以通过对象来调用。先创建一个对象,然后被对象调用,print(对象名.类属性名)

小结:也就是说类属性在定义的时候,不用init方法,不用self关键字。调用的时候,如果是自己调,就直接,类名.类属性名。如果被对象调用,就是print(对象名.类属性名)。

2、类方法

定义:先用@classmethod修饰,然后def 方法名即可,方法的第一个形参是cls

调用:(1)在类的内部:如果在普通方法中:类名.类方法名( );如果在类方法中,cls.类方法名( )

       (2)在类的外部:自己调自己就是,类名.类方法名( )。 注意,如果方法中有return就写成:print(类名.类方法名( ))。如果被对象调用,就是,先创建一个对象,然后,print(对象名.类方法名())来被对象调用。

小结:类方法中,只能访问类属性和类方法,不能访问普通属性和普通方法。因为类方法不需要创建对象,直接通过类名就能调用了。而普通方法可以访问类方法和类属性。在普通方法中,直接通过类名.类属性名或者类名.类方法名( )进行访问。

3、类属性、类方法的使用场景

常用来计数。

例如:判断类被实例化了几次。

class Dog:
    index = 0  # 定义一个类属性

    @classmethod  # 定义一个类方法,名字叫count
    def count(cls):
        return cls.index

    def __init__(self):
        Dog.index += 1  # 调用类属性:【类名.类属性名】


d = Dog()
print(Dog.count())

结果:
1


d1 = Dog()
d2 = Dog()
d3 = Dog()
print(Dog.count())
结果:
3

4、表格式总结类属性、类方法的定义和调用

例如:现在有一个name属性和一个show_name方法,分别把它们定义成普通属性、普通方法和类属性、类方法。

定义

在类的内部调用

在类的外部调用

普通属性

class Person:

   def __init__(self,name=”张三”):

       self.name=name

self.name

即:【self.属性名】

p1=Person()

print(p1.name)

即:先创建对象,再使用【对象名.属性名】的方式调用。

普通方法

def show_name(self):

      print(self.name)

self.show_name()

即:【self.方法名()】

p1=Person()

p1.show_name()

即:先创建对象,再【对象名.方法名】

注意:普通方法可以访问类属性类方法。

例如:访问类属性

print(类名.类属性名) 

Person.类方法名() #调用类方法

类属性

class Person:

        name=”张三”

如果被类方法调用:【cls.类属性名】

如果被普通方法调用:【类名.类属性名】

两种方式:

(1)【类名.类属性名】

(2)【对象名.类属性名】

即:

print(Person.name)或

print(p1.name)

类方法

@classmethod

def show_name(cls):

      print(cls.name)

如果被类方法调用:

【cls.类方法名()】

如果被普通方法调用:

【类名.类方法名()】

两种方式:

(1)【类名.类方法名()】

(2)【对象名.类方法名()】

即:Person.show_name()或【p1.show_name()】

注意:类方法只能访问类属性、类方法

代码展示:

例如:把name属性和show_name方法,定义成普通属性和普通方法

class Person:
    def __init__(self, name="张三"):  # 定义一个普通属性
        self.name = name

    def show_name(self):  # 定义一个普通方法
        print(self.name)  # 普通方法调用普通属性:【self.普通属性名】


p1 = Person()  # 创建对象、为类Person创建一个实例
print(p1.name)  # 对象调用类的普通属性 【对象名.普通属性名】
p1.show_name()  # 对象调用类的普通方法:【对象名.普通方法名】

结果:
张三
张三

把name属性和show_name方法,定义成类属性和类方法

class Person:
    name = "张三"  # 定义一个类属性

    @classmethod  # 定义一个类方法:(1)要用@classmethod修饰。
    def show_name(cls):       # (2)方法的第一个形参是cls。
        print(cls.name)  # 调用类属性:【类名.类属性名】


print(Person.name)  # 调用类属性:【类名.类属性名】
Person.show_name()  # 调用类方法:【类名.类方法名】
结果:
张三
张三

那二者能互相调用吗?普通方法能调用类属性、类方法。但类方法不能调用普通属性和普通方法。

~~~前方高能!!!~~~~

5、【重要!!!】例题演示,类属性、类方法、普通属性、普通方法之间的定义和调用问题。

例如:下面这个代码就清楚的说明了,上面表格中的怎么定义、怎么在内部外部进行调用的问题。

class Dog:
    name = "大黄"  # 类属性

    @classmethod  # 类方法
    def get_name(cls):
        return cls.name  # 调用类属性:【类名.属性名】

    @classmethod  # 类方法
    def demo1(cls):  # 方法的嵌套
        return cls.get_name()  # 调用类方法:【cls.方法名()】

    def __init__(self):
        self.age = 10  # 普通属性

    def get_age(self):  # 普通方法
        return self.age  # 调用普通属性:【self.属性名】

    def demo2(self):   # 普通方法
        print(Dog.name)  # 调用类属性:【类名.属性名】
        return Dog.get_name()   # 调用类方法:【类名.方法名()】


# 第一种情况:先分别调用自己
print(Dog.name)  # 访问类属性:【类名.类属性名】
print(Dog.get_name())  # 访问类方法:【类名.类方法名()】

d = Dog()
print(d.age)  # 访问普通属性:【对象名.属性名】
print(d.get_age())  # 访问普通方法:【对象名.方法名()】
结果:
大黄
大黄
10
10

# 第二种情况:普通方法访问:类属性、类方法
d = Dog()
print(d.demo2())  # 法1:把类属性、类方法都写demo里
结果:
大黄
大黄

#法2:直接用对象调用,不写demo2里
d = Dog()
print(d.name)    # 【对象名.类属性名】
print(d.get_name())   # 【对象名.类方法名()】
结果:
大黄
大黄

下面两个代码也是在说明,普通方法都能调用类属性、类方法,而类方法不能调用普通方法和普通属性,这个问题,尽管以重名为例,但本质是一样的。可以跳过直接看第五节、静态方法。

注意:普通方法和类方法一定不能重名,重名的话会报错例如下面代码:普通方法和类方法重名了,此时,谁写在下面谁就会覆盖上面的,这里就是,普通方法覆盖了类方法,此时要再调用类方法,结果会报错,因为类方法被覆盖了,所以它就不在了,此时再去调用方法名显然调用的是普通方法里的方法。而普通方法的调用方法是,先创建对象,然后通过对象名.方法名才能调用。

class Person:
    name = "张三"  # 类属性

    @classmethod  # 类方法:
    def show_name(cls):
        print(cls.name)  # 调用类属性

    def show_name(self):  # 普通方法和类方法重名
        print("hello")


Person.show_name()  # 调用类方法
结果:
    Person.show_name()
TypeError: show_name() missing 1 required positional argument: 'self'



#因为普通方法写在类方法下面,所以普通方法会覆盖类方法
#此时再想调用show_name就只能调用的普通方法里的
#所以需要把Person.show_name()改掉

p1 = Person()
p1.show_name()
结果:
hello

反之,如果普通方法写在上面而类方法写在下面,那么此时,普通方法就被覆盖了,后面再调用的话,只能调用类方法。

class Person:
    name = "张三"  # 类属性

    def show_name(self):
        print("hello")

    @classmethod  # 类方法:
    def show_name(cls):
        print(cls.name)  # 调用类属性

p1 = Person()
p1.show_name() # 普通方法被覆盖了,所以调用的是同名的类方法
结果:
张三    

#或者直接写成Person.show_name(),结果也是一样。
Person.show_name()
结果:
张三

也就是说:当类方法和普通方法重名时谁在下面谁有效,因为上面的会被覆盖,所以上面的就无效了。即:

(1)当普通方法有效时,只能通过【对象名.方法名】的方式调用。

(2)当类方法有效时,可以通过【对象名.方法名】的方式调用,也可以通过【类名.方法名】的方式调用。

说白了就是,无论重不重名,普通方法都能调用类属性、类方法,而类方法不能调用普通方法和普通属性。

五、静态方法

1、定义

在类中用@staticmethod修饰的方法就叫静态方法。静态方法不能访问类中所有的成员(也就是不能访问所有的属性和方法),它只是一个独立存在于类中的函数。但是类中的方法却可以访问它。

说白了,就是把一个函数放到类里面去,然后用@staticmethod进行修饰,这样的方法就叫静态方法,虽然在类里,但是它不能访问别人,而别人却可以访问它,它自己只是单纯的存在于类中而已。

2、调用

(1)在类的外部调用:类名.静态方法名()

(2)在类的内部调用:self.静态方法名()

例如:

class A:
    @staticmethod  # 定义一个静态方法test
    def test():
        print("静态方法1")

    @classmethod
    def demo2(cls):
        cls.test()  # 类方法调用静态方法

    def demo1(self):
        self.test()  # 普通方法调用静态方法


A.demo2()  # 调用类方法:【类名.类方法名】
a = A()  # 创建类A的实例
a.demo1()  # 调用普通方法:【对象名.普通方法名】

结果:
静态方法1
静态方法1

3、使用场景

当一个项目特别大的时候,代码量就会特别大,用到的函数也就会特别多,为了避免由于函数重名而产生的错误,这时候,就可以把同名的函数放到不同的类里面去,让它作为静态方法,然后再去调用它,这样就不会出错了。

例如:有两个同名的test函数,功能分别是:

    def test():
        print("静态方法1")


    def test():
        print("静态方法2")

为了让这两个函数都有效,都不被覆盖,就可以使用静态方法。把它们分别放到两个类里面去,然后再分别用@staticmethod进行修饰即可。

例如:把它们分别放到类A和类B里面去。

class A:
    @staticmethod
    def test():
        print("静态方法1")


class B:
    @staticmethod
    def test():
        print("静态方法2")


A.test()
B.test()

结果:
静态方法1
静态方法2

可以看到,俩test方法都能成功调用了。

当然,也可以不用静态方法,直接把它们分别放到两个类里面去也可以,只是在调用的时候会比较麻烦,需要创建对象然后再通过对象名.方法名调用

例如:把上面的代码进行改进,不用静态方法。

class A:
    def test(self):  # 定义一个方法
        print("静态方法1")


class B:
    def test(self):  # 定义一个方法
        print("静态方法2")


a = A()  # 为类A创建实例
a.test()  # 调用A里的test方法

b = B()  # 为类B创建实例
b.test()  # 调用类B里的test方法
结果:
静态方法1
静态方法2

结果也是一样的,但是显然,用静态方法使用起来会更方便些,不用创建对象了。

六、object类

python3中,如果一个类定义的时候,没写父类,那就默认父类就是object,它是Python自带的。而python2中,如果没写,那就代表没有父类,不会像python3这样,会自动继承object。

所以python3中写类的时候,object写不写都行,不写就代表默认object,写了也不算错误。

例如:定义一个Dog类,两种写法都对,但推荐1 。

#写法1—————— 推荐
class Dog:  
    pass


#写法2
class Dog(object):
    pass

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python面向对象程序设计三大特点:封装、继承、多态、类方法@classmethod、类属性、静态方法@staticmethod、object类 的相关文章

  • matplotlib:调整图形窗口大小而不缩放图形内容

    当您调整图形大小时 Matplotlib 会自动缩放图形窗口中的所有内容 通常这是用户想要的 但我经常想增加窗口的大小 为其他东西腾出更多空间 在这种情况下 我希望在更改窗口大小时预先存在的内容保持相同的大小 有谁知道一个干净的方法来做到这
  • 根据另一个数据框中找到的范围填充数据框中的列

    我试图根据该记录的索引值是否落在另一个数据框中的两列定义的范围内来填充数据框中的列 df1 看起来像 a 0 4 1 45 2 7 3 5 4 48 5 44 6 22 7 89 8 45 9 44 10 23 df2 是 START ST
  • 如何将 Google Cloud Storage 中的许多文件设为私有?

    我进行了很多研究 但无法为此提出解决方案 以下是我用来在 GCP 中公开所有文件的代码 def make blob public bucket name blob name Makes a blob publicly accessible
  • 类型错误:translate() 只接受一个参数(给定 2 个参数)[重复]

    这个问题在这里已经有答案了 我的代码在 python 2 x 版本上运行良好 但是当我尝试在 python 3 x 版本上运行它时 出现错误 主题 需要缩写短信编码中的任何消息 Code def sms encoding data star
  • 在Python中清理属于不同语言的文本

    我有一个文本集合 其中的句子要么完全是英语 印地语或马拉地语 每个句子附加的 id 为 0 1 2 分别代表文本的语言 无论任何语言的文本都可能有 HTML 标签 标点符号等 我可以使用下面的代码清理英语句子 import HTMLPars
  • 在 Python 中使用类作为命名空间是个好主意吗

    我正在将一堆相关的东西放入一个类中 主要目的是将它们组织到命名空间中 class Direction north 0 east 1 south 2 west 3 staticmethod def turn right d return tu
  • Python ElementTree 获取带有命名空间的属性

    我试图访问 XML 中的 def 所以在这个例子中我会得到Evolus Common PlainTextV2作为输出 我似乎无法弄清楚如何获取具有名称空间的属性 如果我想得到id它工作得很好 Python for content ns in
  • 如何在每次运行 python 程序时添加新列

    我希望我的表的第一列作为卷号 第二列作为名称 每当我运行 python 程序时 我想在表中添加一列日期 在这个新列中 我想填充从 user list 获得的列表将包含值 P A P P 等 如何处理 我尝试首先通过 alter 命令添加一列
  • 为什么在 __init__ 函数中声明描述符类会破坏描述符功能?

    在下面的 B 类中 我想要 set 每当您赋值给 A 类中的函数时 就会调用该函数B a 相反 将值设置为B a覆盖B a与价值 C类分配给C a工作正常 但我想为每个用户类都有一个单独的 A 实例 即我不想在 C 的一个实例中更改 a 来
  • Python 在 64 位 vista 上获取 os.environ["ProgramFiles"] 的错误值

    Vista64 计算机上的 Python 2 4 3 环境中有以下2个变量 ProgramFiles C Program Files ProgramFiles x86 C Program Files x86 但是当我运行以下命令时 impo
  • Python Tkinter 网格复选框

    我想知道是否有一种简单的方法可以使用 Tkinter 创建复选框网格 我正在尝试制作一个由 10 行和 10 列 即 100 个复选框 组成的网格 以便每行只能选择两个复选框 编辑 我正在使用带有spyder的python 2 7 到目前为
  • 使用 Python 导入包含文本和数字数据的文件

    I have a txt file which has text data and numerical data The first two rows of the file have essential information in te
  • 在 Python 中将嵌套字典位置作为参数传递

    如果我有一个嵌套字典 我可以通过索引来获取键 如下所示 gt gt gt d a b c gt gt gt d a b c 我可以将该索引作为函数参数传递吗 def get nested value d path a b return d
  • 如何删除 pip 安装的所有软件包?

    如何从当前激活的虚拟环境中卸载 pip 安装的所有软件包 我发现这个片段作为替代解决方案 与重新创建 virtualenv 相比 删除库更加优雅 pip freeze xargs pip uninstall y 如果您通过 VCS 安装了软
  • 如何在自定义 django 命令中抽象出命令代码

    我正在我的应用程序下编写自定义 django 命令management commands目录 目前我在该目录中有 6 个不同的文件 每个文件都有不同的命令来解决独特的需求 然而 有一些实用程序是它们所共有的 抽象出这些公共代码的最佳方法是什
  • 如何使用 Python 实现并行 gzip 压缩?

    使用python压缩大文件 https stackoverflow com questions 9518705 big file compression with python给出了一个很好的例子来说明如何使用例如bz2 纯粹用 Pytho
  • 列表中的“u”是什么意思?

    这是我第一次遇到这种情况 刚刚打印了一个列表 每个元素似乎都有一个u在它前面 即 u hello u hi u hey 它是什么意思 为什么列表的每个元素前面都会有这个 由于我不知道这种情况有多常见 如果您想了解我是如何遇到它的 我会很乐意
  • datetime strftime 不输出正确的时间戳

    下列 gt gt gt from dateutil parser import parse gt gt gt parse 2013 07 02 00 00 00 0000 datetime datetime 2013 7 2 0 0 tzi
  • 访问影子 DOM 中的元素

    是否有可能查找 Shadow DOM 中的元素与蟒蛇硒 示例用例 我有这个input with type date
  • 具有行业级约束的 SciPy 投资组合优化

    尝试在这里优化投资组合权重分配 通过限制风险来最大化我的回报函数 我可以毫无问题地通过简单的约束 所有权重之和等于 1 找到产生我的回报函数的优化权重 并做出另一个约束 即我的总风险低于目标风险 我的问题是 如何为每个组添加行业权重界限 我

随机推荐