python3知识点杂记(二)

17、==与is

python中对象包括三个基本要素:id(身份标识)、type(数据类型)、value(值)

  • ==是用来比较两个对象的value是否相等
  • is是用来比较两个对象的id是否相等
a = b = [1, 2, 3]
c = [1, 2, 3]

print(a == b == c)  # 值相等
print(a is b)  # a和b的id相等
print(a is c)  # a和c的id不相等

print(id(a), id(b), id(c))
True
True
False
2163829400840 2163829400840 2163829400328
a = 257
b = 257
print(a is b)
print(id(a), id(b))


a1 = 1
a2 = 1
print(a1 is a2)
print(id(a1), id(a2))
False
2163829990704 2163829990736
True
1395683984 1395683984

注意:对于整数对象,python把一些使用比较频繁的整数对象缓存起来,保存到一个叫small_int的链表中,在python的整个生命周期内,任何需要引用这些整数对象的地方,都不会重新创建新的对象,而是直接引用。python规定的整数对象范围为:[-5, 256]。

再看一个例子:
a = 257

def test():
    b = 257
    c = 257
    print(b is c)
    print(a is b)
    print("id_a={0}, id_b={1}, id_c={2}".format(id(a), id(b), id(c)))
    
test()
True
False
id_a=2163829990832, id_b=2163829990800, id_c=2163829990800

按照上例的解释,257不在[-5, 256]内,那么两个的is结果应该都是False才对?

这里需要从代码块的角度解释。python程序是由代码块构成,代码块作为程序的一个最小基本单元来执行,一个模块文件、一个函数体、一个类、交互式命令中的单行代码都叫一个代码块。上面的程序中包括两个代码块,a=257是一个代码块,test()是一个代码块。python为了提升性能,凡是在一个代码块中创建的整数对象,如果在这个代码块中存在一个值与其相同,那么就直接引用,否则创建一个新对象出来(针对不可变类型),所以在test()函数里的b is c是返回True,a和b不在同一个代码块中,python内部创建了两个257的对象。

注意: 在pycharm中执行,得到的结果是True,原因1.py是一个模块文件,在同一个代码块中,值相同的对象不会重复创建。
在这里插入图片描述

18、私有变量

python中,变量的定义方式有:

  • xx:共有变量
  • _xx:私有化属性或方法,类对象和子类可以访问,from yy import * 不能导入(也叫伪私有变量,与__xx区别)
  • __xx:私有属性或方法,无法在外部直接访问(名字重整所以访问不到),也称真私有
  • __xx__:系统定义的名字,不要自己发明这样的名字
  • xx_:用于避免与python关键词的冲突
class A():
    __xx = 4
    _xx = 5
    def __init__(self):
        self.num = 1
        self._num = 2
        self.__num = 3
    def foo(self):
        print(self._xx, self.__xx, self.num, self._num, self.__num)

a = A()
print(a.num)  # 1
print(a._num)  # 2
print(a._xx)
print(A._xx)

# print(a.__xx)  # 外部不能访问私有变量
# print(a.__num)
# print(A.__xx)

a.foo()  # 内部什么都能访问

# 形象的来说,别人只能看到你外在的东西,你自己私有的品质别人是看不到的,只有你自己知道

1
2
5
5
5 4 1 2 3

查看a实例对象的属性,发现__num的名字已经调整为_A__num,所以非要访问这个变量的话,可以a._A__num,但是不建议这样做。

print(a.__dict__)
print(A.__dict__)
{'_A__num': 3, '_num': 2, 'num': 1}
{'__module__': '__main__', '_A__xx': 4, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__dict__': <attribute '__dict__' of 'A' objects>, '__doc__': None, 'foo': <function A.foo at 0x000001F7CF50A488>, '_xx': 5, '__init__': <function A.__init__ at 0x000001F7CF50A6A8>}

导入模块,模块中的私有变量的使用,xxoo.py文件如下:
在这里插入图片描述

from xxoo import *  # 无法导入私有变量

print(num)
f()

# print(_num)  # 报错:name '_num' is not defined
# _f()  # name '_f' is not defined

# print(__num)  # name '__num' is not defined
# __f()  # name '__f' is not defined
1
--test1--

解决办法:

import xxoo

print(xxoo.num)
xxoo.f()

print(xxoo._num)
xxoo._f()

print(xxoo.__num)
xxoo.__f()
1
--test1--
2
--test2--
3
--test3--

如果就是想在外部进行私有属性的操作,常用的方法有两种:1、添加getter和setter方法,类对象通过调用这种方法来对私有属性进行操作;2、property方法

  • 添加getter和setter方法
class A():
    def __init__(self):
        self.__num = 521
    
    def get_num(self):
        return self.__num
    
    def set_num(self, val):
        self.__num = val
    
a = A()

print(a.get_num())

a.set_num(100)
print(a.get_num())
521
100
  • property方法
    • property方法接收四个参数,分别是fget、fset、fdel、doc,分别对应getter方法、setter方法、delete方法以及方法说明,property()方法返回一个property属性,如果a 是 A 的实例,a.x会调用getter方法、 a.x=value会调用setter方法、 del a.x会调用delete方法。
class A():
    def __init__(self):
        self.__num = 521
    
    def get_num(self):
        return self.__num
    
    def set_num(self, val):
        self.__num = val
    num = property(fget=get_num, fset=set_num)

a = A()
print(a.num)

a.num = 100
print(a.num)
521
100
class A():
    def __init__(self):
        self.__num = 521
    
    @property  # 装饰器@property把方法num()转换成与方法名同名的getter方法,“ """这里写方法说明doc"""”是doc参数
    def num(self):
        """这里写方法说明doc"""
        return self.__num
    
    @num.setter  # property装饰的函数num,使用num.setter 就是setter方法,num.deleter
    def num(self, val):
        self.__num = val

a = A()
print(a.num)

a.num = 100
print(a.num)
521
100

19、动态添加属性和方法

动态添加实例属性

class A():
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
a1 = A("老张", 18)
a2 = A("老王", 20)

print(a1.__dict__)
print(a2.__dict__)

# 动态添加实例属性
print("-----分界线-----" * 2)
a1.tel = "110"
a2.addr = "阿富汗"

print("给a1增加属性后,此时a1的实例属性有:{0}".format(a1.__dict__))
print("给a2增加属性后,此时a2的实例属性有:{0}".format(a2.__dict__))
{'age': 18, 'name': '老张'}
{'age': 20, 'name': '老王'}
-----分界线----------分界线-----
给a1增加属性后,此时a1的实例属性有:{'tel': '110', 'age': 18, 'name': '老张'}
给a2增加属性后,此时a2的实例属性有:{'addr': '阿富汗', 'age': 20, 'name': '老王'}

使用 实例名.属性名 给实例对象动态添加属性,这些属性只属于他自己本身,只有他自己可以使用

动态添加类属性

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

A.addr = "USA"

a1 = A("老张", 18)
a2 = A("老王", 20)

print(A.__dict__)
print(a1.name, a1.age, a1.addr)
print(a2.name, a2.age, a2.addr)
{'__module__': '__main__', '__init__': <function A.__init__ at 0x000001F7CF530158>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__dict__': <attribute '__dict__' of 'A' objects>, '__doc__': None, 'addr': 'USA'}
老张 18 USA
老王 20 USA

使用 类名.属性名 给类对象动态添加类属性,这个类的所有对象都可以查询

动态添加方法: 包括:实例方法,类方法和静态方法。

1、动态添加实例方法:

import types

class A():
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def foo(self):
        print("I am foo")

def f(self):
    print("I am f")

a1 = A("老张", 18)
a2 = A("老王", 20)

print(a1.name, a1.age)
a1.foo()
print("-----分界线-----" * 2)
print(a2.name, a2.age)
a2.foo()
print("-----分界线-----" * 2)
# 给a1动态添加实例方法
a1.f = types.MethodType(f, a1)
print(a1.__dict__)
a1.f()
老张 18
I am foo
-----分界线----------分界线-----
老王 20
I am foo
-----分界线----------分界线-----
{'age': 18, 'name': '老张', 'f': <bound method f of <__main__.A object at 0x000001F7CF524C88>>}
I am f

使用 types.MethodType(方法名, 实例名) 给实例对象动态添加实例方法,该方法是属于这个实例,其他实例不能调用。其中这个函数的默认第一个参数是self。

2、动态添加静态方法和类方法

class A():
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def foo(self):
        print("I am foo")

@staticmethod
def static_func():
    print("I am staticmethod")

@classmethod
def class_func(cls):
    print("I am classmethod")

    
A.static_func = static_func
A.class_func = class_func

a1 = A("老张", 18)
a2 = A("老王", 20)

print(A.__dict__)
print("-----分界线-----" * 2)
a1.static_func()
a2.static_func()
A.static_func()
print("-----分界线-----" * 2)
a1.class_func()
a2.class_func()
A.class_func()
{'__module__': '__main__', '__init__': <function A.__init__ at 0x000001F7CF530B70>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__dict__': <attribute '__dict__' of 'A' objects>, '__doc__': None, 'foo': <function A.foo at 0x000001F7CF530AE8>, 'static_func': <staticmethod object at 0x000001F7CF53C160>, 'class_func': <classmethod object at 0x000001F7CF53C198>}
-----分界线----------分界线-----
I am staticmethod
I am staticmethod
I am staticmethod
-----分界线----------分界线-----
I am classmethod
I am classmethod
I am classmethod

使用 类名.静态/类方法名 给类动态添加静态方法和类方法,使用 @staticmethod@classmethod 进行装饰,静态方法无需传入任何参数,类方法默认第一个参数传入cls。这个类及其所有实例对象都可以调用动态添加的静态方法和类方法

20、__slots__限制class属性

上面介绍了如何动态添加实例属性、类属性、实例方法、类方法、静态方法。但是如果我们想限制class的属性怎么办?比如,我们只允许实例对象添加name和age属性。

import types

class A():
    __slots__ = ("name", "age")
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def foo(self):
        print("I am foo")
        
def f(self):
    print("I am f")

@staticmethod
def static_func():
    print("I am staticmethod")

@classmethod
def class_func(cls):
    print("I am classmethod")

    
A.static_func = static_func
A.class_func = class_func

A.addr = "阿富汗"

a1 = A("老张", 18)
a2 = A("老王", 20)

print(a1.name, a1.age, a1.addr)
print(a2.name, a2.age, a2.addr)

a1.foo()
a1.static_func()
a1.class_func()

# a1.tel = "110"  # 报错:'A' object has no attribute 'tel'
# a1.f = types.MethodType(f, a1)  # 报错:'A' object has no attribute 'f'
老张 18 阿富汗
老王 20 阿富汗
I am foo
I am staticmethod
I am classmethod

以上可见:__slots__ 对类对象的动态添加没有任何限制,但是对实例对象进行限制,实例对象的属性和方法只能使用 __slots__ 和类对象中存在的(A.__dict__中存在的)属性和方法,其他一概不能使用。

未完待续…

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值