目录
一、object基类的常见应用场景
在Python中,object函数 本身并不是一个经常直接使用的函数,因为它实际上是一个内置的基类,而不是一个用于创建对象的函数,然而,object类及其子类(包括用户自定义的类)在Python编程中有广泛的应用,常见的应用场景有:
**1、元编程(Metaprogramming):**通过操作类和其他类型对象,你可以在运行时创建、修改或查询这些对象的行为。例如,你可以使用type()函数在运行时动态地创建新的类,这些类可以继承自其他类(包括object)并包含自定义的方法。
2、实现设计模式: object可以作为实现各种设计模式的基类。例如,你可以使用继承和多态性来实现工厂模式、单例模式或观察者模式等。
**3、使用__slots__优化内存使用:**通过在类中定义`slots`属性,你可以限制实例可以拥有的属性,这有助于减少内存使用并提高性能;`slots`必须是一个字符串序列,其中每个字符串对应一个有效的实例变量名。
4、自定义描述符(Descriptors): 描述符是一种具有特殊方法的类,它们可以控制对属性的访问,描述符通常用于实现属性验证、属性存储或属性的动态计算等,描述符的实现通常涉及`get`, `set`, 和 `delete`方法。
**5、实现上下文管理器(Context Managers):**通过实现`enter`和`exit`方法,你可以将对象用作上下文管理器,这允许你使用with语句来管理资源(如文件、网络连接等)的获取和释放。
**6、使用弱引用(Weak References):**通过weakref模块,你可以创建对对象的弱引用,这不会增加对象的引用计数,这在处理循环引用或需要跟踪但不拥有对象的情况时非常有用。
**7、实现自定义的迭代器(Iterators)和生成器(Generators):**通过实现`iter`和`next`方法,你可以创建自定义的迭代器类,生成器函数是创建迭代器的简便方式,它们使用yield语句来产生一系列的值。
**8、多重继承与混合(Mixins):**你可以使用多重继承来组合多个类的功能,混合是一种特殊的多重继承用法,其中一个小型类(mixin)被设计用来给一组不相关的类添加相同的方法,这有助于代码重用和模块化。
**9、特殊的魔法方法:**object类还定义了一些特殊的魔法方法(magic methods),比如`new`用于创建实例,`getattribute`用于获取属性,`setattr`用于设置属性等,虽然这些魔法方法通常在你需要深度控制对象的创建和属性访问时才会用到,但它们的存在为Python的面向对象编程提供了强大的灵活性。
**10、多态性和接口实现:**通过继承和多态性,你可以创建遵循共同接口或协议的类,这使得代码更加灵活和可维护,因为你可以使用这些类的实例而不必关心它们的具体类型。
总之,object类及其子类在Python编程中无处不在,它们是实现自定义数据结构、行为和交互的基础。
二、object基类使用注意事项
在Python中,**object实际上不是一个函数,而是一个内置的基类。**当你提到"使用`object`函数"时,我猜你可能是在讨论与object基类相关的一些概念或实践,以下是在使用object基类时需要注意的一些事项:
**1、无需显式继承:**在Python 3中,所有的类都默认继承自object,因此你不需要(也不应该)在类定义中显式地指定它,例如,`class MyClass:` 和 `class MyClass(object):` 在Python 3中是等效的。
**2、重写特殊方法:**object基类定义了许多特殊方法(双下划线方法),如`init`, `str`, `repr`, `eq`等,当你在自己的类中重写这些方法时,需要确保你理解它们的作用和预期的行为,不正确的实现可能会导致意外的结果或错误。
**3、避免使用内置属性名:**避免在你的类中使用与object或其他内置类型相同的属性名或方法名,这可能会导致冲突或意外的行为。
**4、多重继承:**虽然Python支持多重继承,但在使用多重继承时需要小心,因为它可能导致方法解析顺序(MRO)的问题,确保你了解Python的MRO规则,并仔细测试你的代码以确保它按预期工作。
**5、元编程和反射:**当你使用元编程和反射技术(如`getattr`, `setattr`, `delattr`, `hasattr`等)时,需要格外小心,这些函数允许你动态地访问和修改对象的属性,但如果不正确使用,可能会导致程序崩溃或数据损坏。
**6、避免不必要的继承:**虽然继承是面向对象编程中的一个重要概念,但并不意味着你应该在所有情况下都使用它。有时,使用组合(composition)而不是继承(inheritance)可能更合适,组合允许你将一个对象作为另一个对象的属性,从而避免继承层次过深或复杂的类结构。
**7、使用抽象基类(ABCs):**如果你正在设计一个框架或库,并希望定义一些必须被子类实现的接口,那么可以考虑使用Python的`abc`模块来定义抽象基类(Abstract Base Classes, ABCs),抽象基类允许你定义一些没有具体实现的方法,并要求子类必须提供这些方法的实现,这有助于确保你的框架或库的使用者遵循一定的规范。
**8、理解类型检查:**在Python中,类型检查是可选的,并且通常通过鸭子类型(duck typing)来实现,这意味着只要对象具有所需的方法或属性,就可以被视为具有特定的类型,然而,在某些情况下,你可能需要进行显式的类型检查,在使用isinstance()或type()函数进行类型检查时,需要确保你了解它们的区别和用途,并避免过度依赖类型检查来编写代码。
三、如何用好object基类?
在Python中,**object作为所有类的基类,为类的设计和实现提供了强大的基础,**要有效地使用object基类,需遵循以下建议:
**1、理解继承:**虽然Python 3中的类默认继承自object,但理解继承的概念仍然很重要;通过继承,你可以创建新的类,这些类继承自其他类(包括`object`)的属性和方法,这有助于代码重用和扩展性。
**2、重写特殊方法:**object类定义了许多特殊方法(如`init`, `str`, `eq`等),你可以在自己的类中重写这些方法以定义对象的行为,例如,重写`str`方法可以提供对象的字符串表示形式,这对于调试和日志记录非常有用。
**3、避免不必要的继承:**虽然继承是一个强大的工具,但过度使用可能导致代码复杂性和维护性的增加,在决定是否使用继承时,要仔细考虑是否真的需要继承某个类的属性和方法,或者是否可以通过组合(将对象作为另一个对象的属性)来实现所需的功能。
**4、利用多态性:**由于所有类都继承自object,Python支持多态性,这意味着你可以编写接受任何类型对象的函数或方法,并在运行时根据对象的实际类型来执行不同的操作,这增加了代码的灵活性和可重用性。
**5、使用属性装饰器:**Python提供了`@property`装饰器,它可以用于将方法转换为对象的属性,这使得你可以定义复杂的属性访问逻辑,同时保持属性的访问方式与简单属性相同,这有助于保持代码的清晰和一致性。
**6、理解方法解析顺序(MRO):**当使用多重继承时,理解方法解析顺序(MRO)很重要,MRO决定了当子类调用一个方法时,Python将按照什么顺序搜索基类以找到该方法的定义,了解MRO可以帮助你避免意外地覆盖或跳过方法定义。
**7、设计可扩展的API:**当你设计类库或框架时,考虑使用object作为基类,并定义一些抽象方法或属性,这样,其他开发者可以继承你的类并实现这些方法或属性,从而扩展你的库或框架的功能。
**8、利用Python的内置功能:**除了object类本身,Python还提供了许多内置函数和模块,这些都可以与object一起使用来创建强大的功能,例如,你可以使用dir()函数查看对象的属性列表,使用hasattr()函数检查对象是否具有某个属性等。
总之,要有效地使用Python中的object,你需要深入理解继承、特殊方法、多态性、属性装饰器以及方法解析顺序等概念,通过合理地利用这些概念和功能,你可以创建出灵活、可维护和可扩展的Python代码。
1、object基类:
1-1、Python:
python
# 1.函数:object(基类)
# 2.功能:
# 2-1、定义基本行为:object类定义了一些基本的、通用的方法,如 `__init__`, `__str__`, `__repr__`, `__eq__`, `__ne__` 等,这些方法在创建新类时可以被覆盖或扩展,以提供特定于该类的行为
# 2-2、作为基类:所有用户定义的类都隐式或显式地继承自object,这确保了Python中的所有对象都有一些基本的、通用的方法和属性
# 2-3、多态性支持:由于所有类都继承自object,Python能够支持多态性;多态性意味着不同的对象可以对相同的消息(即方法调用)做出不同的响应
# 2-4、简化类定义:在Python 2中,如果没有显式地继承自任何基类,那么该类会隐式地继承自一个名为"旧式类"的基类,但在Python 3中,所有类都隐式地继承自object,这被称为"新式类",新式类提供了更多的功能和灵活性,例如描述符、多重继承的改进等
# 2-5、元编程和反射:由于object是所有类的基类,因此可以使用诸如 `type()`, `isinstance()`, `issubclass()` 等内置函数来检查和操作对象和类,这使得Python支持元编程和反射,即程序在运行时能够检查和修改其自身的结构或行为
# 3.语法:object
# 4.参数:不接受任何参数
# 5.返回值:返回一个不带特征的新对象
# 6.说明:由于object没有 __dict__,因此无法将任意属性赋给object的实例
# 7.示例:
# 用dir()函数获取该函数内置的属性和方法
print(dir(object))
# ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__',
# '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
# '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
# 用help()函数获取该函数的文档信息
help(object)
# 应用一:元编程(Metaprogramming)
# 示例1:使用type()动态创建类
def create_class(class_name, base_classes=(), attrs={}):
return type(class_name, base_classes, attrs)
# 定义一个方法,稍后将添加到类中
def greet(self):
return f"Hello, I'm {self.name}!"
# 使用type()动态创建一个类
MyClass = create_class("MyClass", (object,), {'name': 'Default Name', 'greet': greet})
# 创建一个实例并调用方法
instance = MyClass()
print(instance.greet())
# Hello, I'm Default Name!
# 示例2:使用setattr()和getattr()在运行时修改对象属性
class DynamicObject(object):
pass
# 创建一个实例
obj = DynamicObject()
# 在运行时设置属性
setattr(obj, 'name', 'Myelsa')
setattr(obj, 'age', 18)
setattr(obj, 'city', 'Guangzhou')
# 在运行时获取属性
print(getattr(obj, 'name'))
print(getattr(obj, 'age', None))
# 尝试获取不存在的属性,并指定默认值
print(getattr(obj, 'city', 'Unknown'))
# Myelsa
# 18
# Guangzhou
# 示例3:使用dir()和hasattr()检查对象属性
class Person(object):
def __init__(self, name):
self.name = name
# 创建一个实例
person = Person('Myelsa')
# 使用dir()列出对象的所有属性(包括方法和内置方法)
print(
dir(person))
# 使用hasattr()检查对象是否具有某个属性
print(hasattr(person, 'name'))
print(hasattr(person, 'age'))
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
# '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__',
# '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name']
# True
# False
# 示例4:使用描述符进行元编程
class Typed:
def __init__(self, name, expected_type):
self.name = name
self.expected_type = expected_type
def __get__(self, instance, owner):
return instance.__dict__.get(self.name, None)
def __set__(self, instance, value):
if not isinstance(value, self.expected_type):
raise TypeError(f"Expected {self.name} to be {self.expected_type}")
instance.__dict__[self.name] = value
def __delete__(self, instance):
if self.name in instance.__dict__:
del instance.__dict__[self.name]
class Person(object):
age = Typed('age', int)
# 创建一个实例并设置属性
person = Person()
person.age = 30 # 正常设置
# 尝试设置错误类型的值
try:
person.age = 'thirty'
except TypeError as e:
print(e)
# Expected age to be <class 'int'>
# 应用二:实现设计模式
# 示例1:单例模式(Singleton Pattern)
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
# 使用
singleton_instance1 = Singleton()
singleton_instance2 = Singleton()
print(singleton_instance1 is singleton_instance2)
# True
# 示例2:工厂模式(Factory Pattern)
class CarFactory(object):
def create_car(self, car_type):
if car_type == 'sedan':
return Sedan()
elif car_type == 'suv':
return SUV()
else:
raise ValueError("Invalid car type")
class Car(object):
def __init__(self):
self.type = None
class Sedan(Car):
def __init__(self):
super().__init__()
self.type = 'sedan'
class SUV(Car):
def __init__(self):
super().__init__()
self.type = 'suv'
# 使用
factory = CarFactory()
sedan = factory.create_car('sedan')
suv = factory.create_car('suv')
print(sedan.type)
print(suv.type)
# sedan
# suv
# 示例3:观察者模式(Observer Pattern)
class Observer(object):
def update(self, subject):
pass
class ConcreteObserver(Observer):
def update(self, subject):
print(f"Received update from {subject.name}")
class Subject(object):
def __init__(self):
self.observers = []
self.state = None
def attach(self, observer):
self.observers.append(observer)
def detach(self, observer):
self.observers.remove(observer)
def notify(self):
for observer in self.observers:
observer.update(self)
def set_state(self, state):
self.state = state
self.notify()
class ConcreteSubject(Subject):
def __init__(self, name):
super().__init__()
self.name = name
# 使用
subject = ConcreteSubject("Weather Station")
observer = ConcreteObserver()
subject.attach(observer)
subject.set_state("Rainy")
# Received update from Weather Station
# 应用三:使用__slots__优化内存使用
class Person(object):
__slots__ = ['name', 'age', 'email']
def __init__(self, name, age, email):
self.name = name
self.age = age
self.email = email
# 创建一个Person实例
p = Person('Myelsa', 18, '94509325@qq.com')
# 访问属性
print(p.name)
print(p.age)
print(p.email)
# 尝试添加不在__slots__中的属性,会抛出AttributeError
# p.address = '123 Street' # 这行会抛出AttributeError
# 如果你想让子类也能添加额外的属性,可以在子类中扩展__slots__
class Employee(Person):
__slots__ = ['employee_id', 'department']
def __init__(self, name, age, email, employee_id, department):
super().__init__(name, age, email)
self.employee_id = employee_id
self.department = department
# 创建一个 Employee 实例
e = Employee('Jimmy', 15, '94509325@qq.com', 1001, 'CTO')
# 访问Employee特有的属性
print(e.employee_id)
print(e.department)
# 尝试添加不在__slots__中的属性到Employee实例,同样会抛出AttributeError
# e.salary = 50000 # 这行会抛出AttributeError
# Myelsa
# 18
# 94509325@qq.com
# 1001
# CTO
# 应用四:自定义描述符(Descriptors)
class TypedDescriptor:
def __init__(self, name, expected_type):
self.name = name
self.expected_type = expected_type
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__.get(self.name, None)
def __set__(self, instance, value):
if not isinstance(value, self.expected_type):
raise TypeError(f"Expected {self.expected_type}, got {type(value)}")
instance.__dict__[self.name] = value
def __delete__(self, instance):
if self.name in instance.__dict__:
del instance.__dict__[self.name]
# 使用自定义描述符的类
class MyClass(object):
x = TypedDescriptor('x', int)
# 创建实例并设置和访问属性
my_instance = MyClass()
my_instance.x = 10 # 设置属性,类型正确
print(my_instance.x) # 访问属性
# 尝试设置错误类型的属性
try:
my_instance.x = 'not an integer' # 这将触发TypeError
except TypeError as e:
print(e)
# 尝试删除属性
del my_instance.x
print(hasattr(my_instance, 'x'))
# 10
# Expected <class 'int'>, got <class 'str'>
# True
# 应用五:实现上下文管理器(Context Managers)
class FileContextManager(object): # 在Python 3中,一般都用class FileContextManager:替代
def __init__(self, file_path, mode='r'):
self.file_path = file_path
self.mode = mode
self.file = None
def __enter__(self):
# 在with语句块执行前打开文件
self.file = open(self.file_path, self.mode)
return self.file # 返回文件对象,以便在with语句块中使用
def __exit__(self, exc_type, exc_val, exc_tb):
# 在with语句块执行后关闭文件
# 如果有异常发生,exc_type, exc_val, exc_tb会被设置
if self.file:
self.file.close()
self.file = None
# 如果需要,可以在这里处理异常
# 例如,如果exc_type不是None,表示有异常发生
# 你可以在这里执行清理操作或重新抛出异常
# 使用上下文管理器
with FileContextManager('file.txt') as file:
content = file.read()
print(content)
# 这里的文件对象在with块结束时会被自动关闭,无需显式调用close()方法
# 121314536273838390
# 123A
# 456B
# 789C
# 587
# 1024
# 应用六:使用弱引用(Weak References)
import weakref
class MyClass:
def __init__(self, name):
self.name = name
def __repr__(self):
return f"<MyClass {self.name}>"
# 创建一个MyClass的实例
obj = MyClass("example")
# 创建一个弱引用到obj
weak_ref = weakref.ref(obj)
# 通过弱引用访问对象
if weak_ref() is not None:
print(weak_ref())
# 移除对obj的所有强引用
del obj
# 尝试再次通过弱引用访问对象
# 注意:此时obj可能已经被垃圾回收,所以weak_ref()可能返回None
print(weak_ref()) # 可能输出: None
# 使用weakref.WeakValueDictionary来存储弱引用
# WeakValueDictionary中的值(不是键)是弱引用
wvd = weakref.WeakValueDictionary()
wvd[1] = MyClass("one")
wvd[2] = MyClass("two")
# 显示WeakValueDictionary的内容
for key, value in wvd.items():
print(key, value) # 输出键和值,值可能是<MyClass ...>或None(如果对象已被回收)
# # 移除对其中一个对象的所有强引用
# del wvd[1]
# 显示更新后的WeakValueDictionary内容
# 注意:此时键1可能已经不再存在
for key, value in wvd.items():
print(key, value)
# <MyClass example>
# None
# 应用七:实现自定义的迭代器(Iterators)和生成器(Generators)
# 示例1:自定义迭代器
class MyIterator(object):
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.current < self.end:
value = self.current
self.current += 1
return value
else:
raise StopIteration
# 使用自定义迭代器
iterator = MyIterator(0, 5)
for item in iterator:
print(item)
# 0
# 1
# 2
# 3
# 4
# 示例2:自定义生成器
def my_generator(start, end):
for i in range(start, end):
yield i
# 使用自定义生成器
for item in my_generator(0, 5):
print(item)
# 0
# 1
# 2
# 3
# 4
# 应用八:多重继承与混合(Mixins)
# 定义第一个父类,包含一些方法
class ParentA:
def __init__(self):
self.a_attribute = "I'm from ParentA"
def method_a(self):
print("This is method_a from ParentA.")
# 定义第二个父类,也包含一些方法
class ParentB:
def __init__(self):
self.b_attribute = "I'm from ParentB"
def method_b(self):
print("This is method_b from ParentB.")
# 定义第三个父类,用于演示与其他父类不相关的功能
class MixinC:
def __init__(self):
self.c_attribute = "I'm a mixin!"
def method_c(self):
print("This is method_c from MixinC.")
# 定义子类,继承自ParentA、ParentB和MixinC
class Child(ParentA, ParentB, MixinC):
def __init__(self):
# 调用父类的__init__方法以确保初始化它们的属性
# 注意:Python中的多重继承可能会导致方法解析顺序(MRO)问题,这里需要明确指定调用顺序
ParentA.__init__(self)
ParentB.__init__(self)
MixinC.__init__(self)
def method_child(self):
print("This is method_child from Child.")
# 实例化子类并调用方法
child_instance = Child()
print(child_instance.a_attribute)
print(child_instance.b_attribute)
print(child_instance.c_attribute)
child_instance.method_a()
child_instance.method_b()
child_instance.method_c()
child_instance.method_child()
# I'm from ParentA
# I'm from ParentB
# I'm a mixin!
# This is method_a from ParentA.
# This is method_b from ParentB.
# This is method_c from MixinC.
# This is method_child from Child.
# 应用九:特殊的魔法方法
# 1. 初始化对象:__init__
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
person = Person("Myelsa", 18)
print(person.name, person.age)
# Myelsa 18
# 2. 表示对象:__str__ 和 __repr__
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Person({self.name}, {self.age})"
def __repr__(self):
return f"Person(name={self.name}, age={self.age})"
person = Person("Myelsa", 18)
print(person) # 使用 __str__,输出: Person(Myelsa, 18)
print(repr(person)) # 使用 __repr__,输出: Person(name=Myelsa, age=18)
# Person(Myelsa, 18)
# Person(name=Myelsa, age=18)
# 3. 自定义比较:__eq__, __lt__, __gt__ 等
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __eq__(self, other):
if isinstance(other, Person):
return self.age == other.age
return False
def __lt__(self, other):
if isinstance(other, Person):
return self.age < other.age
return NotImplemented
alice = Person("Myelsa", 18)
bob = Person("Jimmy", 15)
print(alice == bob) # 使用 __eq__,输出: False
print(alice > bob) # 使用 __lt__ 的相反逻辑,输出: True
# False
# True
# 4. 访问控制:__getattr__, __setattr__, __delattr__
class Person:
def __init__(self, name):
self._name = name # 使用下划线前缀作为内部属性
def __getattr__(self, name):
if name == 'name':
return self._name
raise AttributeError(f"'Person' object has no attribute '{name}'")
def __setattr__(self, name, value):
if name == 'name':
self._name = value
else:
super().__setattr__(name, value)
def __delattr__(self, name):
if name == 'name':
raise AttributeError("Can't delete 'name' attribute")
super().__delattr__(name)
person = Person("Myelsa")
print(person.name) # 使用 __getattr__,输出: Myelsa
person.name = "Bruce" # 使用 __setattr__
print(person.name) # 输出: Bruce
# del person.name # 这会触发 __delattr__ 并引发AttributeError
# Myelsa
# Bruce
# 5. 容器类型:__len__, __getitem__, __setitem__, __delitem__
class MyList:
def __init__(self):
self.items = []
def __len__(self):
return len(self.items)
def __getitem__(self, index):
return self.items[index]
def __setitem__(self, index, value):
self.items[index] = value
def __delitem__(self, index):
del self.items[index]
my_list = MyList()
my_list[0] = "Alice"
my_list[1] = "Bob"
print(len(my_list)) # 使用 __len__,输出: 2
print(my_list[0]) # 使用 __getitem__,输出: Alice
del my_list[1] # 使用 __delitem__
print(len(my_list)) # 输出: 1
# 应用十:多态性和接口实现
# 定义一个接口类(实际上是一个抽象基类)
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
@abstractmethod
def perimeter(self):
pass
# 实现接口的类:圆形
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
def perimeter(self):
return 2 * 3.14 * self.radius
# 实现接口的类:矩形
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
# 使用多态性:创建一个函数,该函数可以处理任何实现了Shape接口的类
def calculate_area_and_perimeter(shape):
print(f"Area: {shape.area()}")
print(f"Perimeter: {shape.perimeter()}")
# 创建Circle和Rectangle的实例,并使用上面的函数
circle = Circle(5)
rectangle = Rectangle(4, 6)
calculate_area_and_perimeter(circle)
calculate_area_and_perimeter(rectangle)
# Area: 78.5
# Perimeter: 31.400000000000002
# Area: 24
# Perimeter: 20
1-2、VBA:
vbnet
略,待后补。
2、推荐阅读:
2-1、************Python-VBA函数之旅-globals()函数****************
Python算法之旅:************************************************************Algorithm****************************************************************
Python函数之旅:************************************************************Functions****************************************************************